PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / admin / settings / fields / EmbedModelSelect.js

EmbedModelSelect.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/settings/fields/EmbedModelSelect.js

211 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useCallback, useEffect, useState } from 'react';
2 import { __, sprintf } from "@wordpress/i18n";
3 import ReactSelect from "react-select";
4 import {
5 isArray,
6 isObject,
7 merge,
8 useBuilderContext,
9 useOptions,
10 valueExists,
11 when,
12 withLabel,
13 wpFetch
14 } from 'quickbuilder';
15
16 import Tippy from '@tippyjs/react'; // Install with `npm install @tippyjs/react`
17 import 'tippy.js/dist/tippy.css'; // Tippy.js styles
18 import '../../scss/model-select.scss';
19
20 const CustomOption = (props) => {
21 const {
22 data,
23 innerRef,
24 innerProps,
25 isFocused,
26 isSelected,
27 selectProps, // Contains classNamePrefix
28 } = props;
29
30 // Construct class names using classNamePrefix
31 const prefix = selectProps.classNamePrefix || 'react-select';
32 const optionClassName = `${prefix}__option`;
33 const focusedClassName = isFocused ? `${optionClassName}--is-focused` : '';
34 const selectedClassName = isSelected ? `${optionClassName}--is-selected` : '';
35
36 return (
37 <div
38 ref={innerRef}
39 {...innerProps}
40 className={`${optionClassName} ${focusedClassName} ${selectedClassName} custom-option`}
41 style={{
42 cursor: 'pointer',
43
44 }}
45 >
46 <span>{data.label}</span>
47 {data.tooltip && (
48 <Tippy content={data.tooltip} placement='right'>
49 <span className="tooltip-icon" style={{ marginLeft: '0px' }}>
50 <svg
51 width="14"
52 height="14"
53 viewBox="0 0 14 14"
54 fill="none"
55 xmlns="http://www.w3.org/2000/svg"
56 >
57 <path
58 d="M6.33203 10.332H7.66536V6.33203H6.33203V10.332ZM6.9987 4.9987C7.18759 4.9987 7.34592 4.93481 7.4737 4.80703C7.60148 4.67925 7.66536 4.52092 7.66536 4.33203C7.66536 4.14314 7.60148 3.98481 7.4737 3.85703C7.34592 3.72925 7.18759 3.66536 6.9987 3.66536C6.80981 3.66536 6.65148 3.72925 6.5237 3.85703C6.39592 3.98481 6.33203 4.14314 6.33203 4.33203C6.33203 4.52092 6.39592 4.67925 6.5237 4.80703C6.65148 4.93481 6.80981 4.9987 6.9987 4.9987ZM6.9987 13.6654C6.07648 13.6654 5.20981 13.4904 4.3987 13.1404C3.58759 12.7904 2.88203 12.3154 2.28203 11.7154C1.68203 11.1154 1.20703 10.4098 0.857031 9.5987C0.507031 8.78759 0.332031 7.92092 0.332031 6.9987C0.332031 6.07648 0.507031 5.20981 0.857031 4.3987C1.20703 3.58759 1.68203 2.88203 2.28203 2.28203C2.88203 1.68203 3.58759 1.20703 4.3987 0.857031C5.20981 0.507031 6.07648 0.332031 6.9987 0.332031C7.92092 0.332031 8.78759 0.507031 9.5987 0.857031C10.4098 1.20703 11.1154 1.68203 11.7154 2.28203C12.3154 2.88203 12.7904 3.58759 13.1404 4.3987C13.4904 5.20981 13.6654 6.07648 13.6654 6.9987C13.6654 7.92092 13.4904 8.78759 13.1404 9.5987C12.7904 10.4098 12.3154 11.1154 11.7154 11.7154C11.1154 12.3154 10.4098 12.7904 9.5987 13.1404C8.78759 13.4904 7.92092 13.6654 6.9987 13.6654ZM6.9987 12.332C8.48759 12.332 9.7487 11.8154 10.782 10.782C11.8154 9.7487 12.332 8.48759 12.332 6.9987C12.332 5.50981 11.8154 4.2487 10.782 3.21536C9.7487 2.18203 8.48759 1.66536 6.9987 1.66536C5.50981 1.66536 4.2487 2.18203 3.21536 3.21536C2.18203 4.2487 1.66536 5.50981 1.66536 6.9987C1.66536 8.48759 2.18203 9.7487 3.21536 10.782C4.2487 11.8154 5.50981 12.332 6.9987 12.332Z"
59 fill="#667085"
60 />
61 </svg>
62 </span>
63 </Tippy>
64 )}
65 </div>
66 );
67 };
68
69
70 const EmbedModelSelect = (props) => {
71 const builderContext = useBuilderContext();
72 let { id, name, multiple, placeholder, search = false, onChange, parentIndex } = props;
73 const { options, selectedOption, setOptions, setData } = useOptions(props, 'options');
74 const [sOption, setSOption] = useState(null);
75 const [isLoading, setIsLoading] = useState(false);
76 const [isAjaxComplete, setIsAjaxComplete] = useState(false);
77
78 const handleMenuOpen = () => {
79 if (props.ajax && (!props.ajax.rules || when(props.ajax.rules, builderContext.values))) {
80 setIsLoading(true);
81 let data = {};
82 Object.keys(props?.ajax.data).map(singleData => {
83 if (props?.ajax.data[singleData].indexOf('@') > -1) {
84 let eligibleKey = props?.ajax.data[singleData].substr(1);
85 data[singleData] = builderContext.values?.[eligibleKey];
86 } else {
87 data[singleData] = props?.ajax.data[singleData];
88 }
89 });
90 if (!isAjaxComplete) {
91 return wpFetch({
92 path: props?.ajax.api,
93 data: data
94 }).then((response) => {
95 setIsLoading(false);
96 const arrayMerge = merge(props.options, response, 'value');
97 builderContext.setFormField([...parentIndex, 'options'], arrayMerge);
98 setData({
99 options: arrayMerge,
100 parentIndex: [...parentIndex, 'options']
101 });
102 return response;
103 });
104 }
105 }
106 };
107
108 const handleMenuClose = () => {
109 setIsLoading(false);
110 };
111
112 useEffect(() => {
113 if (!isArray(sOption) && isObject(sOption)) {
114 onChange({
115 target: {
116 type: 'select',
117 name,
118 value: sOption.value,
119 options,
120 multiple
121 },
122 });
123 }
124 if (isArray(sOption)) {
125 onChange({
126 target: {
127 type: 'select',
128 name,
129 value: sOption.map(item => item.value),
130 options,
131 multiple
132 },
133 });
134 }
135 }, [sOption]);
136
137 useEffect(() => {
138 handleMenuOpen();
139 }, []);
140
141 useEffect(() => {
142 if (props?.menuOpen) {
143 handleMenuOpen();
144 }
145 }, [props?.menuOpen]);
146
147 const handleOptionChange = useCallback((option) => {
148 if (isArray(option) && props?.filterValue?.length > 0) {
149 const origialValues = option;
150 let values = origialValues;
151
152 let filterValue = props?.filterValue ?? ['all'];
153 if (!isArray(filterValue)) {
154 filterValue = [filterValue];
155 }
156
157 if (origialValues?.length > 1 && valueExists(origialValues.map(item => item.value), filterValue)) {
158 values = origialValues.filter((item) => !filterValue.includes(item?.value));
159 }
160
161 option = values;
162 }
163
164 setSOption(option);
165 }, [name, id, parentIndex]);
166
167 // Conditional components prop
168 const selectComponents = props.options_tooltip
169 ? { Option: CustomOption }
170 : undefined;
171
172 return (
173 <div className="wprf-select-wrapper">
174 <ReactSelect
175 isDisabled={props?.disable}
176 className="wprf-select"
177 classNamePrefix="wprf-select"
178 isSearchable={search ?? false}
179 id={id}
180 name={name}
181 isMulti={multiple ?? false}
182 placeholder={placeholder}
183 isLoading={isLoading}
184 options={options}
185 value={selectedOption}
186 onMenuOpen={handleMenuOpen}
187 onMenuClose={handleMenuClose}
188 isOptionDisabled={(option) => option?.disabled}
189 onChange={handleOptionChange}
190 components={selectComponents} // Conditional rendering of tooltip-enabled options
191
192 />
193
194 {betterdocs_admin?.count_new_docs > 0 && (
195 <div className='model-wrapper'>
196 <div className="sync-docs-wrapper">
197 <a href={`?page=betterdocs-settings&sync-new-docs=1&_nonce=${betterdocs_admin?.sync_nonce}`}>
198 {sprintf(
199 __('Sync pending posts (%d)', 'betterdocs'),
200 betterdocs_admin?.count_new_docs
201 )}
202 </a>
203 </div>
204 </div>
205 )}
206 </div>
207 );
208 };
209
210 export default withLabel(EmbedModelSelect);
211