@siratik/wp-component-library 0.1.1 → 0.1.3
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/LICENSE +5 -0
- package/README.md +43 -5
- package/components/attachment-image/README.md +118 -0
- package/components/attachment-image/index.js +105 -0
- package/components/attributes-panel/README.md +92 -0
- package/components/attributes-panel/index.js +324 -0
- package/components/badge/README.md +61 -0
- package/components/badge/index.js +105 -0
- package/components/block-appender/README.md +158 -0
- package/components/block-appender/index.js +135 -0
- package/components/block-extension/index.js +214 -0
- package/components/business-hours/SpecialHoursControl.js +211 -0
- package/components/business-hours/WeeklyHoursControl.js +419 -0
- package/components/business-hours/index.js +8 -0
- package/components/button/README.md +172 -0
- package/components/button/index.js +84 -0
- package/components/captcha-placeholder/CloudflareTurnstile.js +111 -0
- package/components/captcha-placeholder/GoogleRecaptcha.js +132 -0
- package/components/captcha-placeholder/HCaptcha.js +99 -0
- package/components/captcha-placeholder/README.md +228 -0
- package/components/captcha-placeholder/index.js +39 -0
- package/components/captcha-placeholder/styles.js +33 -0
- package/components/geocoder/AddressEntryControl.js +390 -0
- package/components/geocoder/GeocoderSearchControl.js +353 -0
- package/components/geocoder/countryOptions.js +66 -0
- package/components/geocoder/geocoder.js +400 -0
- package/components/geocoder/index.js +16 -0
- package/components/icon-picker/Icon.js +53 -0
- package/components/icon-picker/IconPicker.js +70 -0
- package/components/icon-picker/IconPickerModal.js +474 -0
- package/components/icon-picker/IconPickerToolbarButton.js +60 -0
- package/components/icon-picker/InlineIconPicker.js +92 -0
- package/components/icon-picker/api.js +181 -0
- package/components/icon-picker/config-bootstrap.js +108 -0
- package/components/icon-picker/hooks.js +157 -0
- package/components/icon-picker/index.js +39 -0
- package/components/icon-picker/load-script.js +33 -0
- package/components/icon-picker/loaders.js +79 -0
- package/components/icon-picker/store.js +223 -0
- package/components/icon-picker/styles.js +232 -0
- package/components/icon-picker/utils.js +138 -0
- package/components/layout/README.md +268 -0
- package/components/link-tools-panel/README.md +49 -0
- package/components/link-tools-panel/index.js +198 -0
- package/components/link-tools-panel/styles.js +90 -0
- package/components/media/README.md +288 -0
- package/components/media/SettingsMediaUpload.js +186 -0
- package/components/media/editor-media-upload.js +65 -0
- package/components/media/image-controls.js +196 -0
- package/components/media/index.js +13 -0
- package/components/media/inspector-media-upload.js +290 -0
- package/components/media/toolbar-media-upload.js +53 -0
- package/components/media/utils/aspect-ratios.js +118 -0
- package/components/meta/MetaPanel.js +75 -0
- package/components/meta/README.md +46 -0
- package/components/meta/index.js +6 -0
- package/components/meta/meta-gated-editor.js +282 -0
- package/components/query/README.md +340 -0
- package/components/query/cardTemplatePartPanel.js +69 -0
- package/components/query/cardTemplatePartSelect.js +165 -0
- package/components/query/cardTemplatePartUtils.js +119 -0
- package/components/query/columnCountControl.js +63 -0
- package/components/query/displayTypeSelect.js +52 -0
- package/components/query/examples.js +166 -0
- package/components/query/hideCurrentPostControl.js +47 -0
- package/components/query/index.js +20 -0
- package/components/query/manualPostSelector.js +125 -0
- package/components/query/manualTermSelector.js +141 -0
- package/components/query/orderBySelect.js +95 -0
- package/components/query/postTypeSelect.js +67 -0
- package/components/query/postsPerPageControl.js +51 -0
- package/components/query/selectionModeControl.js +61 -0
- package/components/query/taxonomySelect.js +139 -0
- package/components/query/utils.js +75 -0
- package/components/repeater/README.md +159 -0
- package/components/repeater/index.js +829 -0
- package/components/segmented-bar/README.md +30 -0
- package/components/segmented-bar/index.js +68 -0
- package/components/sortable-select/README.md +153 -0
- package/components/sortable-select/custom-sortable-token.js +127 -0
- package/components/sortable-select/index.js +693 -0
- package/components/style-picker/README.md +36 -0
- package/components/style-picker/index.js +92 -0
- package/components/variable-field/README.md +299 -0
- package/components/variable-field/index.js +592 -0
- package/components/variable-field/value-to-chip-html.js +38 -0
- package/components/variable-inserter/README.md +68 -0
- package/components/variable-inserter/index.js +168 -0
- package/constants/statusColors.js +1 -1
- package/editor.js +16 -0
- package/index.js +127 -0
- package/package.json +21 -13
- package/utils/kitaris-localize.js +128 -0
- package/utils/slug-aware-color-setter.js +27 -0
- package/build/index.asset.php +0 -20
- package/build/index.js +0 -1242
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RangeControl,
|
|
3
|
+
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
4
|
+
} from "@wordpress/components";
|
|
5
|
+
import { __ } from "@wordpress/i18n";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Standalone column count control
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props Component props
|
|
11
|
+
* @param {Object} props.attributes Block attributes
|
|
12
|
+
* @param {Function} props.setAttributes Function to set attributes
|
|
13
|
+
* @param {string} props.displayAs Current display type to determine label
|
|
14
|
+
* @param {string} props.label Label override (default: from displayAs)
|
|
15
|
+
* @param {number} props.defaultValue The block's default column count (default: 3)
|
|
16
|
+
* @param {number} props.min Minimum value
|
|
17
|
+
* @param {number} props.max Maximum value
|
|
18
|
+
* @param {boolean} props.isShownByDefault Whether to show by default in ToolsPanel (default: false)
|
|
19
|
+
* @returns {WPElement} Element to render
|
|
20
|
+
*/
|
|
21
|
+
function ColumnCountControl({
|
|
22
|
+
attributes: { columnCount, displayAs },
|
|
23
|
+
setAttributes,
|
|
24
|
+
min = 1,
|
|
25
|
+
max = 6,
|
|
26
|
+
isShownByDefault = false,
|
|
27
|
+
label,
|
|
28
|
+
defaultValue = 3,
|
|
29
|
+
}) {
|
|
30
|
+
// Don't render for list or pills display types
|
|
31
|
+
if (displayAs === "list" || displayAs === "pills") {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const itemLabel =
|
|
36
|
+
label ||
|
|
37
|
+
(displayAs === "grid"
|
|
38
|
+
? __("Columns", "wp-component-library")
|
|
39
|
+
: __("Slides to show", "wp-component-library"));
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<ToolsPanelItem
|
|
43
|
+
hasValue={() => columnCount !== defaultValue}
|
|
44
|
+
label={itemLabel}
|
|
45
|
+
onDeselect={() => setAttributes({ columnCount: defaultValue })}
|
|
46
|
+
isShownByDefault={isShownByDefault}
|
|
47
|
+
>
|
|
48
|
+
<RangeControl
|
|
49
|
+
__nextHasNoMarginBottom={true}
|
|
50
|
+
__next40pxDefaultSize
|
|
51
|
+
label={itemLabel}
|
|
52
|
+
value={columnCount}
|
|
53
|
+
onChange={(columnCountNew) =>
|
|
54
|
+
setAttributes({ columnCount: columnCountNew })
|
|
55
|
+
}
|
|
56
|
+
min={min}
|
|
57
|
+
max={max}
|
|
58
|
+
/>
|
|
59
|
+
</ToolsPanelItem>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { ColumnCountControl };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SelectControl,
|
|
3
|
+
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
4
|
+
} from "@wordpress/components";
|
|
5
|
+
import { __ } from "@wordpress/i18n";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Standalone display type selection control
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props Component props
|
|
11
|
+
* @param {Object} props.attributes Block attributes
|
|
12
|
+
* @param {Function} props.setAttributes Function to set attributes
|
|
13
|
+
* @param {Array} props.allowedDisplayOptions Allowed display options
|
|
14
|
+
* @param {boolean} props.isShownByDefault Whether to show by default in ToolsPanel (default: true)
|
|
15
|
+
* @returns {WPElement} Element to render
|
|
16
|
+
*/
|
|
17
|
+
function DisplayTypeSelect({
|
|
18
|
+
attributes: { displayAs },
|
|
19
|
+
setAttributes,
|
|
20
|
+
allowedDisplayOptions = ["grid", "slider", "list", "pills"],
|
|
21
|
+
isShownByDefault = true,
|
|
22
|
+
}) {
|
|
23
|
+
const defaultDisplayAs = "grid";
|
|
24
|
+
|
|
25
|
+
const displayOptions = [
|
|
26
|
+
{ label: "Grid", value: "grid" },
|
|
27
|
+
{ label: "Slider", value: "slider" },
|
|
28
|
+
{ label: "List", value: "list" },
|
|
29
|
+
{ label: "Pills", value: "pills" },
|
|
30
|
+
{ label: "Marquee", value: "marquee" },
|
|
31
|
+
].filter((option) => allowedDisplayOptions.includes(option.value));
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ToolsPanelItem
|
|
35
|
+
hasValue={() => displayAs !== defaultDisplayAs}
|
|
36
|
+
label={__("Display Type", "built_starter")}
|
|
37
|
+
onDeselect={() => setAttributes({ displayAs: defaultDisplayAs })}
|
|
38
|
+
isShownByDefault={isShownByDefault}
|
|
39
|
+
>
|
|
40
|
+
<SelectControl
|
|
41
|
+
__nextHasNoMarginBottom={true}
|
|
42
|
+
__next40pxDefaultSize
|
|
43
|
+
label="Display As"
|
|
44
|
+
value={displayAs}
|
|
45
|
+
options={displayOptions}
|
|
46
|
+
onChange={(value) => setAttributes({ displayAs: value })}
|
|
47
|
+
/>
|
|
48
|
+
</ToolsPanelItem>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { DisplayTypeSelect };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { __experimentalToolsPanel as ToolsPanel } from "@wordpress/components";
|
|
2
|
+
import { __ } from "@wordpress/i18n";
|
|
3
|
+
import {
|
|
4
|
+
ColumnCountControl,
|
|
5
|
+
DisplayTypeSelect,
|
|
6
|
+
ManualPostSelector,
|
|
7
|
+
OrderBySelect,
|
|
8
|
+
PostsPerPageControl,
|
|
9
|
+
PostTypeSelect,
|
|
10
|
+
SelectionModeControl,
|
|
11
|
+
TaxonomySelect,
|
|
12
|
+
TermSelector,
|
|
13
|
+
} from "./";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Example: Basic Post Query Interface
|
|
17
|
+
* Simple interface with just post selection and ordering
|
|
18
|
+
*/
|
|
19
|
+
export function BasicPostQueryExample({ attributes, setAttributes }) {
|
|
20
|
+
const resetAll = () => {
|
|
21
|
+
setAttributes({
|
|
22
|
+
postType: "post",
|
|
23
|
+
orderPostsBy: "date",
|
|
24
|
+
orderPostsDirection: "desc",
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ToolsPanel
|
|
30
|
+
label={__("Post Query", "built_starter")}
|
|
31
|
+
resetAll={resetAll}
|
|
32
|
+
>
|
|
33
|
+
<PostTypeSelect
|
|
34
|
+
attributes={attributes}
|
|
35
|
+
setAttributes={setAttributes}
|
|
36
|
+
/>
|
|
37
|
+
<OrderBySelect
|
|
38
|
+
attributes={attributes}
|
|
39
|
+
setAttributes={setAttributes}
|
|
40
|
+
/>
|
|
41
|
+
</ToolsPanel>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Example: Advanced Query Interface with Manual Selection
|
|
47
|
+
* Complete interface with manual post selection and taxonomy filtering
|
|
48
|
+
*/
|
|
49
|
+
export function AdvancedQueryExample({ attributes, setAttributes }) {
|
|
50
|
+
const resetAll = () => {
|
|
51
|
+
setAttributes({
|
|
52
|
+
postType: "post",
|
|
53
|
+
orderPostsBy: "date",
|
|
54
|
+
orderPostsDirection: "desc",
|
|
55
|
+
selectionMode: "auto",
|
|
56
|
+
selectedPosts: [],
|
|
57
|
+
selectedTaxonomy: "",
|
|
58
|
+
selectedTerms: [],
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<ToolsPanel
|
|
64
|
+
label={__("Advanced Query", "built_starter")}
|
|
65
|
+
resetAll={resetAll}
|
|
66
|
+
>
|
|
67
|
+
<PostTypeSelect
|
|
68
|
+
attributes={attributes}
|
|
69
|
+
setAttributes={setAttributes}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
<OrderBySelect
|
|
73
|
+
attributes={attributes}
|
|
74
|
+
setAttributes={setAttributes}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<SelectionModeControl
|
|
78
|
+
attributes={attributes}
|
|
79
|
+
setAttributes={setAttributes}
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
{attributes.selectionMode === "manual" && (
|
|
83
|
+
<ManualPostSelector
|
|
84
|
+
attributes={attributes}
|
|
85
|
+
setAttributes={setAttributes}
|
|
86
|
+
postType={attributes.postType}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
<TaxonomySelect
|
|
91
|
+
attributes={attributes}
|
|
92
|
+
setAttributes={setAttributes}
|
|
93
|
+
postType={attributes.postType}
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
{attributes.selectedTaxonomy && (
|
|
97
|
+
<TermSelector
|
|
98
|
+
attributes={attributes}
|
|
99
|
+
setAttributes={setAttributes}
|
|
100
|
+
selectedTaxonomy={attributes.selectedTaxonomy}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
</ToolsPanel>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Example: Display Settings Interface
|
|
109
|
+
* Just the display-related controls
|
|
110
|
+
*/
|
|
111
|
+
export function DisplaySettingsExample({ attributes, setAttributes }) {
|
|
112
|
+
const resetAll = () => {
|
|
113
|
+
setAttributes({
|
|
114
|
+
displayAs: "grid",
|
|
115
|
+
postsPerPage: 6,
|
|
116
|
+
columnCount: 3,
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<ToolsPanel
|
|
122
|
+
label={__("Display Settings", "built_starter")}
|
|
123
|
+
resetAll={resetAll}
|
|
124
|
+
>
|
|
125
|
+
<DisplayTypeSelect
|
|
126
|
+
attributes={attributes}
|
|
127
|
+
setAttributes={setAttributes}
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
<PostsPerPageControl
|
|
131
|
+
attributes={attributes}
|
|
132
|
+
setAttributes={setAttributes}
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
<ColumnCountControl
|
|
136
|
+
attributes={attributes}
|
|
137
|
+
setAttributes={setAttributes}
|
|
138
|
+
/>
|
|
139
|
+
</ToolsPanel>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Example: Raw Controls (No Panels)
|
|
145
|
+
* Using controls without ToolsPanel wrappers
|
|
146
|
+
*/
|
|
147
|
+
export function RawControlsExample({ attributes, setAttributes }) {
|
|
148
|
+
return (
|
|
149
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
150
|
+
<PostTypeSelect
|
|
151
|
+
attributes={attributes}
|
|
152
|
+
setAttributes={setAttributes}
|
|
153
|
+
/>
|
|
154
|
+
|
|
155
|
+
<OrderBySelect
|
|
156
|
+
attributes={attributes}
|
|
157
|
+
setAttributes={setAttributes}
|
|
158
|
+
/>
|
|
159
|
+
|
|
160
|
+
<DisplayTypeSelect
|
|
161
|
+
attributes={attributes}
|
|
162
|
+
setAttributes={setAttributes}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToggleControl,
|
|
3
|
+
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
4
|
+
} from "@wordpress/components";
|
|
5
|
+
import { __ } from "@wordpress/i18n";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Toggle to exclude the post currently being viewed from feed query results.
|
|
9
|
+
* Intended for single templates where the same layout renders every post.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} props Component props
|
|
12
|
+
* @param {Object} props.attributes Block attributes
|
|
13
|
+
* @param {Function} props.setAttributes Function to set attributes
|
|
14
|
+
* @param {boolean} props.isShownByDefault Whether to show by default in ToolsPanel (default: true)
|
|
15
|
+
* @returns {WPElement} Element to render
|
|
16
|
+
*/
|
|
17
|
+
function HideCurrentPostControl({
|
|
18
|
+
attributes: { hideCurrentPost = true },
|
|
19
|
+
setAttributes,
|
|
20
|
+
isShownByDefault = true,
|
|
21
|
+
}) {
|
|
22
|
+
const defaultHideCurrentPost = true;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ToolsPanelItem
|
|
26
|
+
hasValue={() => hideCurrentPost !== defaultHideCurrentPost}
|
|
27
|
+
label={__("Hide Current Post", "built_starter")}
|
|
28
|
+
onDeselect={() =>
|
|
29
|
+
setAttributes({ hideCurrentPost: defaultHideCurrentPost })
|
|
30
|
+
}
|
|
31
|
+
isShownByDefault={isShownByDefault}
|
|
32
|
+
>
|
|
33
|
+
<ToggleControl
|
|
34
|
+
__nextHasNoMarginBottom={true}
|
|
35
|
+
label={__("Hide Current Post", "built_starter")}
|
|
36
|
+
checked={hideCurrentPost}
|
|
37
|
+
onChange={(value) => setAttributes({ hideCurrentPost: value })}
|
|
38
|
+
help={__(
|
|
39
|
+
"On single posts and templates, exclude the post being viewed from this feed.",
|
|
40
|
+
"built_starter",
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
</ToolsPanelItem>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { HideCurrentPostControl };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Individual Query Controls - Always render as ToolsPanelItems
|
|
2
|
+
export { ColumnCountControl } from "./columnCountControl";
|
|
3
|
+
export { DisplayTypeSelect } from "./displayTypeSelect";
|
|
4
|
+
export { HideCurrentPostControl } from "./hideCurrentPostControl";
|
|
5
|
+
export { ManualPostSelector } from "./manualPostSelector";
|
|
6
|
+
export { ManualTermSelector } from "./manualTermSelector";
|
|
7
|
+
export { defaultOrderOptions, OrderBySelect } from "./orderBySelect";
|
|
8
|
+
export { PostsPerPageControl } from "./postsPerPageControl";
|
|
9
|
+
export { PostTypeSelect } from "./postTypeSelect";
|
|
10
|
+
export { CardTemplatePartSelect } from "./cardTemplatePartSelect";
|
|
11
|
+
export { CardTemplatePartPanel } from "./cardTemplatePartPanel";
|
|
12
|
+
export {
|
|
13
|
+
buildCardTemplatePartOptions,
|
|
14
|
+
cardTemplatePartMatchesPostType,
|
|
15
|
+
getCardSlugPrefixesForPostType,
|
|
16
|
+
getDefaultCardSlugForPostType,
|
|
17
|
+
} from "./cardTemplatePartUtils";
|
|
18
|
+
export { SelectionModeControl } from "./selectionModeControl";
|
|
19
|
+
export { TaxonomySelect } from "./taxonomySelect";
|
|
20
|
+
export { reorderByIds, useOrderedTerms } from "./utils";
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { __experimentalToolsPanelItem as ToolsPanelItem } from "@wordpress/components";
|
|
2
|
+
import { useSelect } from "@wordpress/data";
|
|
3
|
+
import { useCallback, useMemo } from "@wordpress/element";
|
|
4
|
+
import { __ } from "@wordpress/i18n";
|
|
5
|
+
import SortableSelect from "../sortable-select";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Standalone manual post selection control
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props Component props
|
|
11
|
+
* @param {Object} props.attributes Block attributes
|
|
12
|
+
* @param {Function} props.setAttributes Function to set attributes
|
|
13
|
+
* @param {string} props.postType The post type to select from
|
|
14
|
+
* @param {boolean} props.isShownByDefault Whether to show by default in ToolsPanel (default: true)
|
|
15
|
+
* @returns {WPElement} Element to render
|
|
16
|
+
*/
|
|
17
|
+
function ManualPostSelector({
|
|
18
|
+
attributes: { selectedPosts = [] },
|
|
19
|
+
setAttributes,
|
|
20
|
+
postType = "post",
|
|
21
|
+
isShownByDefault = true,
|
|
22
|
+
}) {
|
|
23
|
+
const defaultSelectedPosts = [];
|
|
24
|
+
|
|
25
|
+
// Fetch selected posts data
|
|
26
|
+
const selectedPostsData = useSelect(
|
|
27
|
+
(select) => {
|
|
28
|
+
if (!postType || selectedPosts.length === 0) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
select("core").getEntityRecords("postType", postType, {
|
|
34
|
+
include: selectedPosts,
|
|
35
|
+
per_page: selectedPosts.length,
|
|
36
|
+
}) || []
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
[postType, selectedPosts],
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Load posts with search
|
|
43
|
+
const loadPosts = useCallback(
|
|
44
|
+
async (inputValue) => {
|
|
45
|
+
if (!postType) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const query = {
|
|
51
|
+
per_page: 20,
|
|
52
|
+
status: "publish",
|
|
53
|
+
search: inputValue || '',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Use resolveSelect for async data fetching
|
|
57
|
+
const posts = await wp.data.resolveSelect("core").getEntityRecords(
|
|
58
|
+
"postType",
|
|
59
|
+
postType,
|
|
60
|
+
query,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return posts || [];
|
|
64
|
+
} catch (error) {
|
|
65
|
+
// eslint-disable-next-line no-console -- surface REST failures to developers.
|
|
66
|
+
console.error('Error loading posts:', error);
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[postType],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Handle selection change
|
|
74
|
+
const handleChange = useCallback(
|
|
75
|
+
(newSelection) => {
|
|
76
|
+
const postIds = newSelection.map((post) => post.id);
|
|
77
|
+
setAttributes({ selectedPosts: postIds });
|
|
78
|
+
},
|
|
79
|
+
[setAttributes],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Format selected value for SortableSelect - maintain order
|
|
83
|
+
const selectedValue = useMemo(() => {
|
|
84
|
+
return selectedPosts
|
|
85
|
+
.map((postId) =>
|
|
86
|
+
selectedPostsData.find((post) => post.id === postId),
|
|
87
|
+
)
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
}, [selectedPostsData, selectedPosts]);
|
|
90
|
+
|
|
91
|
+
const control = (
|
|
92
|
+
<SortableSelect
|
|
93
|
+
value={selectedValue}
|
|
94
|
+
onChange={handleChange}
|
|
95
|
+
loadOptions={loadPosts}
|
|
96
|
+
getOptionLabel={(post) => post.title?.rendered || `Post #${post.id}`}
|
|
97
|
+
getOptionValue={(post) => post.id}
|
|
98
|
+
placeholder={__("Type to search posts…", "built")}
|
|
99
|
+
label={__("Select Posts", "built")}
|
|
100
|
+
help={__(
|
|
101
|
+
"Start typing to search for posts, then select them from the dropdown. Drag to reorder.",
|
|
102
|
+
"built",
|
|
103
|
+
)}
|
|
104
|
+
isMulti={true}
|
|
105
|
+
isClearable={false}
|
|
106
|
+
isSearchable={true}
|
|
107
|
+
hideDropdownIndicator={true}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<ToolsPanelItem
|
|
113
|
+
hasValue={() => selectedPosts && selectedPosts.length > 0}
|
|
114
|
+
label={__("Select Posts", "built")}
|
|
115
|
+
onDeselect={() =>
|
|
116
|
+
setAttributes({ selectedPosts: defaultSelectedPosts })
|
|
117
|
+
}
|
|
118
|
+
isShownByDefault={isShownByDefault}
|
|
119
|
+
>
|
|
120
|
+
{control}
|
|
121
|
+
</ToolsPanelItem>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { ManualPostSelector };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { __experimentalToolsPanelItem as ToolsPanelItem } from "@wordpress/components";
|
|
2
|
+
import { useSelect } from "@wordpress/data";
|
|
3
|
+
import { useCallback, useMemo } from "@wordpress/element";
|
|
4
|
+
import { __ } from "@wordpress/i18n";
|
|
5
|
+
import SortableSelect from "../sortable-select";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Standalone term selection control
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props Component props
|
|
11
|
+
* @param {Object} props.attributes Block attributes
|
|
12
|
+
* @param {Function} props.setAttributes Function to set attributes
|
|
13
|
+
* @param {string} props.selectedTaxonomy The taxonomy to select terms from
|
|
14
|
+
* @param {boolean} props.renderAsToolsPanelItem Whether to render as ToolsPanelItem
|
|
15
|
+
* @param {boolean} props.isShownByDefault Whether to show by default in ToolsPanel (default: true)
|
|
16
|
+
* @returns {WPElement} Element to render
|
|
17
|
+
*/
|
|
18
|
+
function ManualTermSelector({
|
|
19
|
+
attributes: { selectedTerms = [] },
|
|
20
|
+
setAttributes,
|
|
21
|
+
selectedTaxonomy,
|
|
22
|
+
renderAsToolsPanelItem = true,
|
|
23
|
+
isShownByDefault = true,
|
|
24
|
+
}) {
|
|
25
|
+
const defaultSelectedTerms = [];
|
|
26
|
+
|
|
27
|
+
// Fetch selected terms data
|
|
28
|
+
const selectedTermsData = useSelect(
|
|
29
|
+
(select) => {
|
|
30
|
+
if (!selectedTaxonomy || selectedTerms.length === 0) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
select("core").getEntityRecords("taxonomy", selectedTaxonomy, {
|
|
36
|
+
include: selectedTerms,
|
|
37
|
+
per_page: selectedTerms.length,
|
|
38
|
+
}) || []
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
[selectedTaxonomy, selectedTerms],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Load terms with search
|
|
45
|
+
const loadTerms = useCallback(
|
|
46
|
+
async (inputValue) => {
|
|
47
|
+
if (!selectedTaxonomy) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const query = {
|
|
53
|
+
per_page: 20,
|
|
54
|
+
hide_empty: false,
|
|
55
|
+
search: inputValue || '',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Use resolveSelect for async data fetching
|
|
59
|
+
const terms = await wp.data.resolveSelect("core").getEntityRecords(
|
|
60
|
+
"taxonomy",
|
|
61
|
+
selectedTaxonomy,
|
|
62
|
+
query,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return terms || [];
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// eslint-disable-next-line no-console -- surface REST failures to developers.
|
|
68
|
+
console.error('Error loading terms:', error);
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[selectedTaxonomy],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Handle selection change
|
|
76
|
+
const handleChange = useCallback(
|
|
77
|
+
(newSelection) => {
|
|
78
|
+
const termIds = newSelection.map((term) => term.id);
|
|
79
|
+
setAttributes({ selectedTerms: termIds });
|
|
80
|
+
},
|
|
81
|
+
[setAttributes],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Format selected value for SortableSelect - maintain order
|
|
85
|
+
const selectedValue = useMemo(() => {
|
|
86
|
+
return selectedTerms
|
|
87
|
+
.map((termId) =>
|
|
88
|
+
selectedTermsData.find((term) => term.id === termId),
|
|
89
|
+
)
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
}, [selectedTermsData, selectedTerms]);
|
|
92
|
+
|
|
93
|
+
const control = (
|
|
94
|
+
<SortableSelect
|
|
95
|
+
value={selectedValue}
|
|
96
|
+
onChange={handleChange}
|
|
97
|
+
loadOptions={loadTerms}
|
|
98
|
+
getOptionLabel={(term) => term.name || `Term #${term.id}`}
|
|
99
|
+
getOptionValue={(term) => term.id}
|
|
100
|
+
placeholder={__("Type to search terms…", "built")}
|
|
101
|
+
label={__("Select Terms", "built")}
|
|
102
|
+
help={__(
|
|
103
|
+
"Start typing to search for taxonomy terms, then select them from the dropdown. Drag to reorder.",
|
|
104
|
+
"built",
|
|
105
|
+
)}
|
|
106
|
+
isMulti={true}
|
|
107
|
+
isClearable={false}
|
|
108
|
+
isSearchable={true}
|
|
109
|
+
hideDropdownIndicator={true}
|
|
110
|
+
renderSelectedItem={(term) => (
|
|
111
|
+
<span>
|
|
112
|
+
{term.name}
|
|
113
|
+
{term.count !== undefined && (
|
|
114
|
+
<span style={{ color: "#666", marginLeft: "4px" }}>
|
|
115
|
+
({term.count})
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
118
|
+
</span>
|
|
119
|
+
)}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!renderAsToolsPanelItem) {
|
|
124
|
+
return control;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<ToolsPanelItem
|
|
129
|
+
hasValue={() => selectedTerms && selectedTerms.length > 0}
|
|
130
|
+
label={__("Select Terms", "built")}
|
|
131
|
+
onDeselect={() =>
|
|
132
|
+
setAttributes({ selectedTerms: defaultSelectedTerms })
|
|
133
|
+
}
|
|
134
|
+
isShownByDefault={isShownByDefault}
|
|
135
|
+
>
|
|
136
|
+
{control}
|
|
137
|
+
</ToolsPanelItem>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { ManualTermSelector };
|