This better not break again for at least a year. (#702)
This commit is contained in:
parent
7e92fd94a3
commit
a6184f57db
4 changed files with 123 additions and 77 deletions
|
|
@ -1351,12 +1351,16 @@ export default definePlugin({
|
|||
]
|
||||
},
|
||||
{
|
||||
// Adds a feedback prop to the SearchableSelect component which will display on invalid searches.
|
||||
// MARK: TODO
|
||||
// - Cleanup once Discord rolls out the new mana select completely.
|
||||
// - Also see anywhere DynamicDropdown is used for refactoring.
|
||||
//
|
||||
// Various patches to the SearchableSelect component.
|
||||
find: '"onSearchChange",',
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Extracts the custom dropdown prop before the variable is overwritten.
|
||||
// Extracts a custom feedback prop before the variable is overwritten.
|
||||
match: /(?<=forwardRef\(function\((\i),\i\){)/,
|
||||
replace: "const vcDynamicDropdownFeedback=$1.feedback;"
|
||||
},
|
||||
|
|
@ -1374,6 +1378,32 @@ export default definePlugin({
|
|||
// Prevent SearchableSelect from force-scrolling into view, causing the dropdown to close.
|
||||
match: /(?<=\.scrollIntoView\()/,
|
||||
replace: "{block:\"nearest\",inline:\"nearest\"}"
|
||||
},
|
||||
{
|
||||
// Passes a popoutClassName and optionClassName to the popout handler.
|
||||
match: /(?<=renderOptionPrefix:\i,renderOptionSuffix:\i)/,
|
||||
replace: ",popoutClassName:arguments[0]?.popoutClassName,optionClassName:arguments[0]?.optionClassName"
|
||||
},
|
||||
{
|
||||
// Makes use of the custom popoutClassName prop if provided.
|
||||
match: /(?<=onKeyDown"]\);return.{0,30}?className:\i\(\)\()/,
|
||||
replace: "arguments[0]?.popoutClassName,"
|
||||
},
|
||||
{
|
||||
// Passes the custom optionClassName prop to the row renderer.
|
||||
match: /(?<="aria-posinset":\i,"aria-setsize":\i.length,)/,
|
||||
replace: "optionClassName:arguments[0]?.optionClassName,"
|
||||
},
|
||||
{
|
||||
// Makes use of the custom optionClassName prop if provided.
|
||||
match: /(?<=focusProps:{enabled:!1},className:\i\(\)\()/,
|
||||
replace: "arguments[0]?.optionClassName,"
|
||||
},
|
||||
{
|
||||
// Pass the unused props to the new mana select being
|
||||
// used by dev://experiment/2025-09-mana-desktop-select
|
||||
match: /(?<=closeOnSelect:\i)(?=})/,
|
||||
replace: ",...arguments[0]"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ function QuestButtonSettings(): JSX.Element {
|
|||
{ label: "Profile Collectibles", value: "collectibles", selected: questRewardIncludeCollectibles },
|
||||
];
|
||||
|
||||
const [currentRewardsOptions, setCurrentRewardsOptions] = useState(questButtonRewardDisplayOptions.filter(option => option.selected));
|
||||
const [currentRewardsOptions, setCurrentRewardsOptions] = useState(questButtonRewardDisplayOptions.filter(option => option.selected).map(option => option.value));
|
||||
const [currentQuestButtonDisplay, setCurrentQuestButtonDisplay] = useState((questButtonDisplayOptions.find(option => option.value === questButtonDisplay) as RadioOption));
|
||||
const [currentQuestButtonUnclaimed, setCurrentQuestButtonUnclaimed] = useState((questButtonUnclaimedOptions.find(option => option.value === questButtonUnclaimed) as RadioOption));
|
||||
const [currentQuestButtonLeftClickAction, setCurrentQuestButtonLeftClickAction] = useState<"open-quests" | "plugin-settings" | "context-menu" | "nothing">(questButtonLeftClickAction as "open-quests" | "plugin-settings" | "context-menu" | "nothing");
|
||||
|
|
@ -457,37 +457,37 @@ function QuestButtonSettings(): JSX.Element {
|
|||
const [currentBadgeColor, setCurrentBadgeColor] = useState((questButtonBadgeColor as number | null));
|
||||
const [dummySelected, setDummySelected] = useState(false);
|
||||
|
||||
function updateSettingsTruthy(enabled: DynamicDropdownSettingOption[]) {
|
||||
const enabledValues = enabled.map(option => option.value);
|
||||
|
||||
function updateSettingsTruthy(enabled: string[]) {
|
||||
questButtonRewardDisplayOptions.forEach(option => {
|
||||
option.selected = enabledValues.includes(option.value);
|
||||
option.selected = enabled.includes(option.value);
|
||||
});
|
||||
|
||||
settings.store.questRewardIncludeRewardCode = enabledValues.includes("reward-code");
|
||||
settings.store.questRewardIncludeNitroCode = enabledValues.includes("nitro-code");
|
||||
settings.store.questRewardIncludeCollectibles = enabledValues.includes("collectibles");
|
||||
settings.store.questRewardIncludeInGame = enabledValues.includes("in-game");
|
||||
settings.store.questRewardIncludeOrbs = enabledValues.includes("orbs");
|
||||
settings.store.questRewardIncludeRewardCode = enabled.includes("reward-code");
|
||||
settings.store.questRewardIncludeNitroCode = enabled.includes("nitro-code");
|
||||
settings.store.questRewardIncludeCollectibles = enabled.includes("collectibles");
|
||||
settings.store.questRewardIncludeInGame = enabled.includes("in-game");
|
||||
settings.store.questRewardIncludeOrbs = enabled.includes("orbs");
|
||||
|
||||
setCurrentRewardsOptions(enabled);
|
||||
validateAndOverwriteIgnoredQuests();
|
||||
}
|
||||
|
||||
function handleQuestRewardDisplayChange(values: Array<DynamicDropdownSettingOption | string>) {
|
||||
const parsedValues = values.map(v => typeof v === "string" ? questButtonRewardDisplayOptions.find(option => option.value === v) as DynamicDropdownSettingOption : v);
|
||||
|
||||
if (values.length === 0) {
|
||||
updateSettingsTruthy([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const stringlessValues = values.filter(v => typeof v !== "string") as DynamicDropdownSettingOption[];
|
||||
const selectedOption = values.find(v => typeof v === "string") as string;
|
||||
const selectedOption = values.length > currentRewardsOptions.length ? values[values.length - 1] as string : currentRewardsOptions.find(v => !values.includes(v)) as string;
|
||||
const stringlessValues = parsedValues.filter(v => v.value !== selectedOption);
|
||||
const option = questButtonRewardDisplayOptions.find(option => option.value === selectedOption) as DynamicDropdownSettingOption;
|
||||
|
||||
if (option.selected) {
|
||||
updateSettingsTruthy(stringlessValues.filter(v => v.value !== selectedOption));
|
||||
updateSettingsTruthy(stringlessValues.filter(v => v.value !== selectedOption).map(v => v.value));
|
||||
} else {
|
||||
updateSettingsTruthy([...stringlessValues, option]);
|
||||
updateSettingsTruthy([...stringlessValues.map(v => v.value), option.value]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -641,7 +641,7 @@ function QuestButtonSettings(): JSX.Element {
|
|||
<Heading className={q("form-subtitle", "form-subtitle-spacier")}>
|
||||
Included Reward Types
|
||||
</Heading>
|
||||
<Paragraph className={q("form-description")}>
|
||||
<Paragraph className={q("form-description", "margin-bottom-8")}>
|
||||
Only count Quests with these reward types as unclaimed when determining button
|
||||
visibility, badge count, and when playing the alert sound.
|
||||
</Paragraph>
|
||||
|
|
@ -649,10 +649,12 @@ function QuestButtonSettings(): JSX.Element {
|
|||
placeholder="Select which reward types to include in the unclaimed count..."
|
||||
feedback="There's no supported Quest feature by that name."
|
||||
className={q("select")}
|
||||
optionClassName={q("affixless-select-popout-option")}
|
||||
maxVisibleItems={questButtonRewardDisplayOptions.length}
|
||||
maxOptionsVisible={questButtonRewardDisplayOptions.length} // Mana Select Prop
|
||||
clearable={true}
|
||||
multi={true}
|
||||
value={currentRewardsOptions as any}
|
||||
value={currentRewardsOptions}
|
||||
options={questButtonRewardDisplayOptions}
|
||||
onChange={handleQuestRewardDisplayChange}
|
||||
closeOnSelect={false}
|
||||
|
|
@ -724,70 +726,73 @@ function DisableQuestsSetting(): JSX.Element {
|
|||
const disableOptions = options.filter(option => option.type === "disable");
|
||||
const modificationOptions = options.filter(option => option.type === "modification");
|
||||
const everythingOnly = options.find(option => option.value === "everything") as DynamicDropdownSettingOption;
|
||||
const [currentValue, setCurrentValue] = useState(options.filter(option => option.selected));
|
||||
|
||||
function updateSettingsTruthy(enabled: DynamicDropdownSettingOption[]) {
|
||||
const enabledValues = enabled.map(option => option.value);
|
||||
const [currentValue, setCurrentValue] = useState(options.filter(option => option.selected).map(option => option.value));
|
||||
|
||||
function updateSettingsTruthy(enabled: string[]) {
|
||||
options.forEach(option => {
|
||||
option.selected = enabledValues.includes(option.value);
|
||||
option.selected = enabled.includes(option.value);
|
||||
});
|
||||
|
||||
const redoAutoFetch = (
|
||||
settings.store.disableQuestsEverything !== enabledValues.includes("everything") ||
|
||||
settings.store.disableQuestsFetchingQuests !== enabledValues.includes("fetching")
|
||||
settings.store.disableQuestsEverything !== enabled.includes("everything") ||
|
||||
settings.store.disableQuestsFetchingQuests !== enabled.includes("fetching")
|
||||
);
|
||||
|
||||
settings.store.disableQuestsEverything = enabledValues.includes("everything");
|
||||
settings.store.disableQuestsDiscoveryTab = enabledValues.includes("discovery");
|
||||
settings.store.disableQuestsFetchingQuests = enabledValues.includes("fetching");
|
||||
settings.store.disableQuestsDirectMessagesTab = enabledValues.includes("dms");
|
||||
settings.store.disableQuestsPageSponsoredBanner = enabledValues.includes("sponsored-banner");
|
||||
settings.store.disableQuestsPopupAboveAccountPanel = enabledValues.includes("popup");
|
||||
settings.store.disableQuestsBadgeOnUserProfiles = enabledValues.includes("badge");
|
||||
settings.store.disableQuestsGiftInventoryRelocationNotice = enabledValues.includes("inventory");
|
||||
settings.store.disableFriendsListActiveNowPromotion = enabledValues.includes("friends-list");
|
||||
settings.store.disableMembersListActivelyPlayingIcon = enabledValues.includes("members-list");
|
||||
settings.store.makeMobileQuestsDesktopCompatible = enabledValues.includes("mobile-desktop-compatible");
|
||||
settings.store.completeVideoQuestsInBackground = enabledValues.includes("video-quests-background");
|
||||
settings.store.completeGameQuestsInBackground = enabledValues.includes("game-quests-background");
|
||||
settings.store.completeAchievementQuestsInBackground = enabledValues.includes("achievement-quests-background");
|
||||
settings.store.notifyOnQuestComplete = enabledValues.includes("notify-on-complete");
|
||||
settings.store.disableQuestsEverything = enabled.includes("everything");
|
||||
settings.store.disableQuestsDiscoveryTab = enabled.includes("discovery");
|
||||
settings.store.disableQuestsFetchingQuests = enabled.includes("fetching");
|
||||
settings.store.disableQuestsDirectMessagesTab = enabled.includes("dms");
|
||||
settings.store.disableQuestsPageSponsoredBanner = enabled.includes("sponsored-banner");
|
||||
settings.store.disableQuestsPopupAboveAccountPanel = enabled.includes("popup");
|
||||
settings.store.disableQuestsBadgeOnUserProfiles = enabled.includes("badge");
|
||||
settings.store.disableQuestsGiftInventoryRelocationNotice = enabled.includes("inventory");
|
||||
settings.store.disableFriendsListActiveNowPromotion = enabled.includes("friends-list");
|
||||
settings.store.disableMembersListActivelyPlayingIcon = enabled.includes("members-list");
|
||||
settings.store.makeMobileQuestsDesktopCompatible = enabled.includes("mobile-desktop-compatible");
|
||||
settings.store.completeVideoQuestsInBackground = enabled.includes("video-quests-background");
|
||||
settings.store.completeGameQuestsInBackground = enabled.includes("game-quests-background");
|
||||
settings.store.completeAchievementQuestsInBackground = enabled.includes("achievement-quests-background");
|
||||
settings.store.notifyOnQuestComplete = enabled.includes("notify-on-complete");
|
||||
|
||||
redoAutoFetch ? checkAutoFetchInterval(settings.store.fetchingQuestsInterval) : null;
|
||||
setCurrentValue(enabled);
|
||||
setCurrentValue(enabled.map(value => typeof value === "string" ? value : (value as any).value));
|
||||
}
|
||||
|
||||
function handleChange(values: Array<DynamicDropdownSettingOption | string>) {
|
||||
const parsedValues = values.map(v => typeof v === "string" ? options.find(option => option.value === v) as DynamicDropdownSettingOption : v);
|
||||
|
||||
if (values.length === 0) {
|
||||
updateSettingsTruthy([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const stringlessValues = values.filter(v => typeof v !== "string") as DynamicDropdownSettingOption[];
|
||||
const selectedOption = values.find(v => typeof v === "string") as string;
|
||||
const selectedOption = values.length > currentValue.length ? values[values.length - 1] as string : currentValue.find(v => !values.includes(v)) as string;
|
||||
const stringlessValues = parsedValues.filter(v => v.value !== selectedOption);
|
||||
|
||||
if (selectedOption === "everything") {
|
||||
if (everythingOnly.selected) { // If was already selected when clicked.
|
||||
updateSettingsTruthy([...stringlessValues.filter(option => option.type !== "disable")]);
|
||||
if (everythingOnly.selected) {
|
||||
// If was already selected when clicked.
|
||||
updateSettingsTruthy(stringlessValues.filter(option => option.value !== selectedOption).map(option => option.value));
|
||||
} else {
|
||||
updateSettingsTruthy([...stringlessValues.filter(option => option.type !== "disable"), everythingOnly]);
|
||||
updateSettingsTruthy([...stringlessValues.filter(option => option.type !== "disable").map(option => option.value), selectedOption]);
|
||||
}
|
||||
} else if (disableOptions.some(option => option.value === selectedOption)) {
|
||||
const option = disableOptions.find(option => option.value === selectedOption) as DynamicDropdownSettingOption;
|
||||
|
||||
if (option.selected) { // If was already selected when clicked.
|
||||
updateSettingsTruthy(stringlessValues.filter(option => option.value !== selectedOption && option.value !== everythingOnly.value));
|
||||
if (option.selected) {
|
||||
// If was already selected when clicked.
|
||||
updateSettingsTruthy(stringlessValues.filter(option => option.value !== selectedOption).map(option => option.value));
|
||||
} else {
|
||||
updateSettingsTruthy([...stringlessValues.filter(option => option.value !== everythingOnly.value), option]);
|
||||
updateSettingsTruthy([...stringlessValues.map(option => option.value), selectedOption]);
|
||||
}
|
||||
} else if (modificationOptions.some(option => option.value === selectedOption)) {
|
||||
const option = modificationOptions.find(option => option.value === selectedOption) as DynamicDropdownSettingOption;
|
||||
|
||||
if (option.selected) { // If was already selected when clicked.
|
||||
updateSettingsTruthy(stringlessValues.filter(option => option.value !== selectedOption));
|
||||
if (option.selected) {
|
||||
// If was already selected when clicked.
|
||||
updateSettingsTruthy(stringlessValues.map(option => option.value));
|
||||
} else {
|
||||
updateSettingsTruthy([...stringlessValues, option]);
|
||||
updateSettingsTruthy([...stringlessValues.map(option => option.value), selectedOption]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -800,7 +805,7 @@ function DisableQuestsSetting(): JSX.Element {
|
|||
<Heading className={q("form-title")}>
|
||||
Quest Features
|
||||
</Heading>
|
||||
<Paragraph className={q("form-description")}>
|
||||
<Paragraph className={q("form-description", "margin-bottom-8")}>
|
||||
Modify specific Quest features.
|
||||
<br /><br />
|
||||
The <span className={q("inline-code-block")}>Disable Quest Popup Above Account Panel</span> option
|
||||
|
|
@ -827,10 +832,12 @@ function DisableQuestsSetting(): JSX.Element {
|
|||
placeholder="Select which Quest features to modify."
|
||||
feedback="There's no supported Quest feature by that name."
|
||||
className={q("select")}
|
||||
optionClassName={q("affixless-select-popout-option")}
|
||||
maxVisibleItems={options.length}
|
||||
maxOptionsVisible={options.length} // Mana Select Prop
|
||||
clearable={true}
|
||||
multi={true}
|
||||
value={currentValue as any}
|
||||
value={currentValue}
|
||||
options={options}
|
||||
onChange={handleChange}
|
||||
closeOnSelect={false}
|
||||
|
|
@ -1350,24 +1357,27 @@ function FetchingQuestsSetting(): JSX.Element {
|
|||
};
|
||||
}
|
||||
|
||||
function getAllIntervalOptions(currentValue: SelectOption) {
|
||||
const otherOptions = resolvedIntervals.filter(option => option.value !== currentValue.value);
|
||||
function getAllIntervalOptions(currentValue: number) {
|
||||
const currentOption = createIntervalSelectOptionFromValue(currentValue);
|
||||
const otherOptions = resolvedIntervals.filter(option => option.value !== currentValue);
|
||||
|
||||
return [
|
||||
currentValue,
|
||||
currentOption,
|
||||
...otherOptions
|
||||
].sort((a, b) => Number(a.value) - Number(b.value));
|
||||
}
|
||||
|
||||
function getAllAlertOptions(currentValue: SelectOption | null) {
|
||||
const otherOptions = currentValue ? resolvedSounds.filter(option => option.value !== currentValue.value) : resolvedSounds;
|
||||
function getAllAlertOptions(currentValue: string | null) {
|
||||
const otherOptions = currentValue ? resolvedSounds.filter(option => option.value !== currentValue) : resolvedSounds;
|
||||
|
||||
if (!currentValue) {
|
||||
return otherOptions.sort((a, b) => a.label.localeCompare(b.label));
|
||||
}
|
||||
|
||||
const currentOption = createAlertSelectOptionFromValue(currentValue);
|
||||
|
||||
return [
|
||||
currentValue,
|
||||
currentOption!,
|
||||
...otherOptions
|
||||
].sort((a, b) => a.label.localeCompare(b.label));
|
||||
}
|
||||
|
|
@ -1466,10 +1476,10 @@ function FetchingQuestsSetting(): JSX.Element {
|
|||
}
|
||||
|
||||
const resolvedIntervalValue = fetchingQuestsInterval;
|
||||
const [currentIntervalSelection, setCurrentSelection] = useState(createIntervalSelectOptionFromValue(resolvedIntervalValue));
|
||||
const [currentIntervalSelection, setCurrentSelection] = useState(resolvedIntervalValue);
|
||||
const [currentIntervalOptions, setCurrentIntervalOptions] = useState(getAllIntervalOptions(currentIntervalSelection));
|
||||
const resolvedAlertValue = fetchingQuestsAlert;
|
||||
const [currentAlertSelection, setCurrentAlertSelection] = useState<SelectOption | null>(createAlertSelectOptionFromValue(resolvedAlertValue));
|
||||
const [currentAlertSelection, setCurrentAlertSelection] = useState<string | null>(resolvedAlertValue);
|
||||
const [currentAlertOptions, setCurrentAlertOptions] = useState(getAllAlertOptions(currentAlertSelection));
|
||||
// Needed to update the playing state of the preview button.
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
|
|
@ -1523,6 +1533,7 @@ function FetchingQuestsSetting(): JSX.Element {
|
|||
feedback="Intervals must be between 30 minutes and 12 hours."
|
||||
className={q("select", "titled-select")}
|
||||
maxVisibleItems={resolvedIntervals.length + 1}
|
||||
maxOptionsVisible={resolvedIntervals.length + 1}
|
||||
clearable={false}
|
||||
multi={false}
|
||||
value={currentIntervalSelection as any}
|
||||
|
|
@ -1531,12 +1542,12 @@ function FetchingQuestsSetting(): JSX.Element {
|
|||
onSearchChange={handleScaleSearchChange}
|
||||
onClose={() => { setCurrentIntervalOptions(getAllIntervalOptions(currentIntervalSelection)); }}
|
||||
onChange={value => {
|
||||
const option = currentIntervalOptions.find(o => o.value === value) as SelectOption;
|
||||
settings.store.fetchingQuestsInterval = option.value as number;
|
||||
const val = value as number;
|
||||
settings.store.fetchingQuestsInterval = val;
|
||||
|
||||
setCurrentSelection(option);
|
||||
setCurrentIntervalOptions(getAllIntervalOptions(option));
|
||||
checkAutoFetchInterval(option.value as number);
|
||||
setCurrentSelection(val);
|
||||
setCurrentIntervalOptions(getAllIntervalOptions(val));
|
||||
checkAutoFetchInterval(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -1563,27 +1574,27 @@ function FetchingQuestsSetting(): JSX.Element {
|
|||
onSearchChange={handleAlertSearchChange}
|
||||
onClose={() => { setCurrentAlertOptions(getAllAlertOptions(currentAlertSelection)); }}
|
||||
onChange={value => {
|
||||
const option = currentAlertOptions.find(o => o.value === value) as SelectOption;
|
||||
settings.store.fetchingQuestsAlert = value ? option.value as string : null as any;
|
||||
const val = value as string | null;
|
||||
settings.store.fetchingQuestsAlert = val as any;
|
||||
|
||||
setCurrentAlertSelection(value ? option : null);
|
||||
setCurrentAlertOptions(getAllAlertOptions(option));
|
||||
setCurrentAlertSelection(val);
|
||||
setCurrentAlertOptions(getAllAlertOptions(val));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={q("inline-group-item", "alert-icon", { "playing-audio": !!isPlaying })}
|
||||
onClick={() => {
|
||||
if (currentAlertSelection?.value) {
|
||||
if (currentAlertSelection) {
|
||||
if (activePlayer.current) {
|
||||
clearActivePlayer();
|
||||
} else {
|
||||
activePlayer.current = playAudio(currentAlertSelection.value as string, { onEnded: clearActivePlayer, volume: settings.store.fetchingQuestsAlertVolume });
|
||||
activePlayer.current = playAudio(currentAlertSelection, { onEnded: clearActivePlayer, volume: settings.store.fetchingQuestsAlertVolume });
|
||||
setIsPlaying(true);
|
||||
}
|
||||
}
|
||||
}}
|
||||
style={{ cursor: currentAlertSelection && currentAlertSelection.value ? "pointer" : "default" }}
|
||||
style={{ cursor: currentAlertSelection ? "pointer" : "default" }}
|
||||
>
|
||||
{SoundIcon(24, 24)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
/* Plugin Settings */
|
||||
.questify-margin-bottom-8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.questify-setting-divider {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
@ -50,11 +54,11 @@
|
|||
border-radius: 3px;
|
||||
min-height: 43px;
|
||||
border: 1px solid var(--border-normal);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.questify-titled-select > div {
|
||||
margin-top: 0;
|
||||
.questify-affixless-select-popout-option {
|
||||
grid-template-areas: "content selectedIcon" !important;
|
||||
grid-template-columns: 1fr auto !important;
|
||||
}
|
||||
|
||||
.questify-volume-slider-container {
|
||||
|
|
|
|||
|
|
@ -343,6 +343,7 @@ export function GuildlessServerListItem({
|
|||
|
||||
export interface DynamicDropdownProps extends React.ComponentProps<typeof SearchableSelect> {
|
||||
feedback?: string;
|
||||
maxOptionsVisible?: number;
|
||||
}
|
||||
|
||||
export const DynamicDropdown: React.FC<DynamicDropdownProps> = props => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue