myoperator-mcp 0.2.364 → 0.2.365
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/dist/index.js +136 -67
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -710,6 +710,7 @@ const badgeVariants = cva(
|
|
|
710
710
|
variant: {
|
|
711
711
|
// Status-based variants (existing)
|
|
712
712
|
active: "bg-semantic-success-surface text-semantic-success-primary",
|
|
713
|
+
information: "bg-semantic-info-surface text-semantic-info-text",
|
|
713
714
|
warning: "bg-semantic-warning-surface text-semantic-warning-primary",
|
|
714
715
|
failed: "bg-semantic-error-surface text-semantic-error-primary",
|
|
715
716
|
disabled: "bg-semantic-bg-ui text-semantic-text-muted",
|
|
@@ -740,6 +741,7 @@ const badgeVariants = cva(
|
|
|
740
741
|
* @example
|
|
741
742
|
* \`\`\`tsx
|
|
742
743
|
* <Badge variant="active">Active</Badge>
|
|
744
|
+
* <Badge variant="information">Coming Soon</Badge>
|
|
743
745
|
* <Badge variant="warning">Warning</Badge>
|
|
744
746
|
* <Badge variant="failed">Failed</Badge>
|
|
745
747
|
* <Badge variant="disabled">Disabled</Badge>
|
|
@@ -8670,6 +8672,7 @@ export { SearchFilter, searchFilterVariants };
|
|
|
8670
8672
|
import { Loader2, Search } from "lucide-react";
|
|
8671
8673
|
|
|
8672
8674
|
import { cn } from "@/lib/utils";
|
|
8675
|
+
import { Input } from "./input";
|
|
8673
8676
|
import {
|
|
8674
8677
|
Select,
|
|
8675
8678
|
SelectContent,
|
|
@@ -8728,6 +8731,12 @@ export interface SelectFieldProps {
|
|
|
8728
8731
|
searchable?: boolean;
|
|
8729
8732
|
/** Search placeholder text */
|
|
8730
8733
|
searchPlaceholder?: string;
|
|
8734
|
+
/**
|
|
8735
|
+
* Render a divider (top border) above each option group and display the
|
|
8736
|
+
* group labels in uppercase with letter-spacing \u2014 matches the Figma
|
|
8737
|
+
* "routing" dropdown style. Only affects grouped options.
|
|
8738
|
+
*/
|
|
8739
|
+
separateGroups?: boolean;
|
|
8731
8740
|
/**
|
|
8732
8741
|
* Controlled search value. When provided, internal search state is
|
|
8733
8742
|
* ignored and the consumer owns the value \u2014 typically used to drive
|
|
@@ -8849,6 +8858,7 @@ const SelectField = React.forwardRef(
|
|
|
8849
8858
|
options,
|
|
8850
8859
|
searchable,
|
|
8851
8860
|
searchPlaceholder = "Search...",
|
|
8861
|
+
separateGroups,
|
|
8852
8862
|
searchValue,
|
|
8853
8863
|
onSearchChange,
|
|
8854
8864
|
wrapperClassName,
|
|
@@ -8869,6 +8879,17 @@ const SelectField = React.forwardRef(
|
|
|
8869
8879
|
const isSearchControlled = searchValue !== undefined;
|
|
8870
8880
|
const effectiveSearchQuery = isSearchControlled ? searchValue : searchQuery;
|
|
8871
8881
|
|
|
8882
|
+
// Track the current selection ourselves so we always know which option is
|
|
8883
|
+
// selected \u2014 even in uncontrolled mode. This lets us keep the selected
|
|
8884
|
+
// option mounted while the search filter hides everything else (see the
|
|
8885
|
+
// filter below), which prevents Radix from blanking the trigger value and
|
|
8886
|
+
// stealing focus off the search input when the selected item unmounts.
|
|
8887
|
+
const isValueControlled = value !== undefined;
|
|
8888
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(
|
|
8889
|
+
defaultValue ?? ""
|
|
8890
|
+
);
|
|
8891
|
+
const selectedValue = isValueControlled ? value : uncontrolledValue;
|
|
8892
|
+
|
|
8872
8893
|
// Combined value change handler that also fires onSelect with full option object.
|
|
8873
8894
|
// When interceptValue returns false, onValueChange is skipped (only onSelect fires).
|
|
8874
8895
|
const handleValueChange = React.useCallback(
|
|
@@ -8877,6 +8898,9 @@ const SelectField = React.forwardRef(
|
|
|
8877
8898
|
|
|
8878
8899
|
if (!intercepted) {
|
|
8879
8900
|
onValueChange?.(newValue);
|
|
8901
|
+
if (!isValueControlled) {
|
|
8902
|
+
setUncontrolledValue(newValue);
|
|
8903
|
+
}
|
|
8880
8904
|
}
|
|
8881
8905
|
|
|
8882
8906
|
if (onSelect) {
|
|
@@ -8886,7 +8910,7 @@ const SelectField = React.forwardRef(
|
|
|
8886
8910
|
}
|
|
8887
8911
|
}
|
|
8888
8912
|
},
|
|
8889
|
-
[onValueChange, onSelect, interceptValue, options]
|
|
8913
|
+
[onValueChange, onSelect, interceptValue, options, isValueControlled]
|
|
8890
8914
|
);
|
|
8891
8915
|
|
|
8892
8916
|
// Support re-selection: fire onSelect when clicking the already-selected
|
|
@@ -8895,11 +8919,11 @@ const SelectField = React.forwardRef(
|
|
|
8895
8919
|
// no-op.
|
|
8896
8920
|
const handleItemClick = React.useCallback(
|
|
8897
8921
|
(option: SelectOption) => {
|
|
8898
|
-
if (option.value ===
|
|
8922
|
+
if (option.value === selectedValue) {
|
|
8899
8923
|
handleValueChange(option.value);
|
|
8900
8924
|
}
|
|
8901
8925
|
},
|
|
8902
|
-
[
|
|
8926
|
+
[selectedValue, handleValueChange]
|
|
8903
8927
|
);
|
|
8904
8928
|
|
|
8905
8929
|
// Derive state from props
|
|
@@ -8914,46 +8938,55 @@ const SelectField = React.forwardRef(
|
|
|
8914
8938
|
// Determine aria-describedby
|
|
8915
8939
|
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
8916
8940
|
|
|
8917
|
-
// Group options by group property
|
|
8941
|
+
// Group options by group property.
|
|
8942
|
+
//
|
|
8943
|
+
// When client-side filtering is active, non-matching options are hidden
|
|
8944
|
+
// (rendered with \`display:none\`) rather than removed from the tree. The
|
|
8945
|
+
// one exception we *must* keep mounted is the currently-selected option:
|
|
8946
|
+
// if Radix's selected item unmounts, the trigger loses its displayed value
|
|
8947
|
+
// and focus is pulled off the search input. \`matchCount\` tracks how many
|
|
8948
|
+
// options are actually visible so the "no results" / footer states stay
|
|
8949
|
+
// accurate.
|
|
8918
8950
|
const groupedOptions = React.useMemo(() => {
|
|
8919
|
-
const groups: Record<string, SelectOption[]> =
|
|
8920
|
-
|
|
8951
|
+
const groups: Record<string, (SelectOption & { hidden?: boolean })[]> =
|
|
8952
|
+
{};
|
|
8953
|
+
const ungrouped: (SelectOption & { hidden?: boolean })[] = [];
|
|
8954
|
+
let matchCount = 0;
|
|
8955
|
+
|
|
8956
|
+
const filtering =
|
|
8957
|
+
!!searchable && !isSearchControlled && !!searchQuery;
|
|
8958
|
+
const query = searchQuery.toLowerCase();
|
|
8921
8959
|
|
|
8922
8960
|
options.forEach((option) => {
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
searchQuery &&
|
|
8930
|
-
!option.label.toLowerCase().includes(searchQuery.toLowerCase())
|
|
8931
|
-
) {
|
|
8961
|
+
const isMatch =
|
|
8962
|
+
!filtering || option.label.toLowerCase().includes(query);
|
|
8963
|
+
const isSelected = option.value === selectedValue && !!selectedValue;
|
|
8964
|
+
|
|
8965
|
+
// Drop only options that neither match the query nor are selected.
|
|
8966
|
+
if (!isMatch && !isSelected) {
|
|
8932
8967
|
return;
|
|
8933
8968
|
}
|
|
8934
8969
|
|
|
8970
|
+
if (isMatch) matchCount++;
|
|
8971
|
+
const entry = { ...option, hidden: !isMatch };
|
|
8972
|
+
|
|
8935
8973
|
if (option.group) {
|
|
8936
8974
|
if (!groups[option.group]) {
|
|
8937
8975
|
groups[option.group] = [];
|
|
8938
8976
|
}
|
|
8939
|
-
groups[option.group].push(
|
|
8977
|
+
groups[option.group].push(entry);
|
|
8940
8978
|
} else {
|
|
8941
|
-
ungrouped.push(
|
|
8979
|
+
ungrouped.push(entry);
|
|
8942
8980
|
}
|
|
8943
8981
|
});
|
|
8944
8982
|
|
|
8945
|
-
return { groups, ungrouped };
|
|
8946
|
-
}, [options, searchable, isSearchControlled, searchQuery]);
|
|
8983
|
+
return { groups, ungrouped, matchCount };
|
|
8984
|
+
}, [options, searchable, isSearchControlled, searchQuery, selectedValue]);
|
|
8947
8985
|
|
|
8948
8986
|
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
8949
8987
|
|
|
8950
|
-
//
|
|
8951
|
-
const totalRendered =
|
|
8952
|
-
groupedOptions.ungrouped.length +
|
|
8953
|
-
Object.values(groupedOptions.groups).reduce(
|
|
8954
|
-
(sum, items) => sum + items.length,
|
|
8955
|
-
0
|
|
8956
|
-
);
|
|
8988
|
+
// Number of *visible* options \u2014 drives the empty-state and footer rows.
|
|
8989
|
+
const totalRendered = groupedOptions.matchCount;
|
|
8957
8990
|
const showEndOfList = hasMore === false && totalRendered > 0 && !loadingMore;
|
|
8958
8991
|
|
|
8959
8992
|
// Handle search input change
|
|
@@ -9009,7 +9042,14 @@ const SelectField = React.forwardRef(
|
|
|
9009
9042
|
ref={ref}
|
|
9010
9043
|
id={selectId}
|
|
9011
9044
|
state={derivedState}
|
|
9012
|
-
className={cn(
|
|
9045
|
+
className={cn(
|
|
9046
|
+
loading && "pr-10",
|
|
9047
|
+
// Figma "routing" style uses a darker (text-muted) placeholder
|
|
9048
|
+
// instead of the default lighter placeholder token.
|
|
9049
|
+
separateGroups &&
|
|
9050
|
+
"[&>span[data-placeholder]]:text-semantic-text-muted",
|
|
9051
|
+
triggerClassName
|
|
9052
|
+
)}
|
|
9013
9053
|
aria-invalid={!!error}
|
|
9014
9054
|
aria-describedby={ariaDescribedBy}
|
|
9015
9055
|
>
|
|
@@ -9024,28 +9064,45 @@ const SelectField = React.forwardRef(
|
|
|
9024
9064
|
className={contentClassName}
|
|
9025
9065
|
>
|
|
9026
9066
|
{/* Search input */}
|
|
9027
|
-
{searchable &&
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9067
|
+
{searchable &&
|
|
9068
|
+
(separateGroups ? (
|
|
9069
|
+
// Figma "routing" style \u2014 a bordered, rounded search box that
|
|
9070
|
+
// reuses the shared Input component (no leading icon).
|
|
9071
|
+
<div className="px-1 pb-1.5">
|
|
9072
|
+
<Input
|
|
9073
|
+
type="text"
|
|
9074
|
+
placeholder={searchPlaceholder}
|
|
9075
|
+
value={effectiveSearchQuery}
|
|
9076
|
+
onChange={handleSearchChange}
|
|
9077
|
+
// Prevent closing dropdown when clicking input
|
|
9078
|
+
onClick={(e) => e.stopPropagation()}
|
|
9079
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
9080
|
+
/>
|
|
9081
|
+
</div>
|
|
9082
|
+
) : (
|
|
9083
|
+
<div className="flex items-center gap-2 px-3 pb-1.5 border-b border-solid border-semantic-border-layout">
|
|
9084
|
+
<Search className="size-4 text-semantic-text-muted shrink-0" />
|
|
9085
|
+
<input
|
|
9086
|
+
type="text"
|
|
9087
|
+
placeholder={searchPlaceholder}
|
|
9088
|
+
value={effectiveSearchQuery}
|
|
9089
|
+
onChange={handleSearchChange}
|
|
9090
|
+
className="w-full h-[42px] text-base text-semantic-text-primary bg-transparent placeholder:text-semantic-text-placeholder focus:outline-none"
|
|
9091
|
+
// Prevent closing dropdown when clicking input
|
|
9092
|
+
onClick={(e) => e.stopPropagation()}
|
|
9093
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
9094
|
+
/>
|
|
9095
|
+
</div>
|
|
9096
|
+
))}
|
|
9042
9097
|
|
|
9043
|
-
{/* Ungrouped options
|
|
9098
|
+
{/* Ungrouped options. Non-matching options stay mounted but hidden
|
|
9099
|
+
(only the selected option is ever kept while filtering). */}
|
|
9044
9100
|
{groupedOptions.ungrouped.map((option) => (
|
|
9045
9101
|
<SelectItem
|
|
9046
9102
|
key={option.value}
|
|
9047
9103
|
value={option.value}
|
|
9048
9104
|
disabled={option.disabled}
|
|
9105
|
+
className={cn(option.hidden && "hidden")}
|
|
9049
9106
|
onPointerUp={() => handleItemClick(option)}
|
|
9050
9107
|
>
|
|
9051
9108
|
{option.label}
|
|
@@ -9055,32 +9112,44 @@ const SelectField = React.forwardRef(
|
|
|
9055
9112
|
{/* Grouped options */}
|
|
9056
9113
|
{hasGroups &&
|
|
9057
9114
|
Object.entries(groupedOptions.groups).map(
|
|
9058
|
-
([groupName, groupOptions]) =>
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9115
|
+
([groupName, groupOptions]) => {
|
|
9116
|
+
// Hide the group label when every option in it is filtered
|
|
9117
|
+
// out (i.e. only a hidden selected item remains).
|
|
9118
|
+
const hasVisible = groupOptions.some((o) => !o.hidden);
|
|
9119
|
+
return (
|
|
9120
|
+
<SelectGroup key={groupName}>
|
|
9121
|
+
{hasVisible && (
|
|
9122
|
+
<SelectLabel
|
|
9123
|
+
className={cn(
|
|
9124
|
+
separateGroups &&
|
|
9125
|
+
"mt-1 border-t border-solid border-semantic-border-layout pt-2.5 uppercase tracking-[0.5px]"
|
|
9126
|
+
)}
|
|
9127
|
+
>
|
|
9128
|
+
{groupName}
|
|
9129
|
+
</SelectLabel>
|
|
9130
|
+
)}
|
|
9131
|
+
{groupOptions.map((option) => (
|
|
9132
|
+
<SelectItem
|
|
9133
|
+
key={option.value}
|
|
9134
|
+
value={option.value}
|
|
9135
|
+
disabled={option.disabled}
|
|
9136
|
+
className={cn(option.hidden && "hidden")}
|
|
9137
|
+
onPointerUp={() => handleItemClick(option)}
|
|
9138
|
+
>
|
|
9139
|
+
{option.label}
|
|
9140
|
+
</SelectItem>
|
|
9141
|
+
))}
|
|
9142
|
+
</SelectGroup>
|
|
9143
|
+
);
|
|
9144
|
+
}
|
|
9073
9145
|
)}
|
|
9074
9146
|
|
|
9075
|
-
{/* No results message */}
|
|
9076
|
-
{searchable &&
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
No results found
|
|
9082
|
-
</div>
|
|
9083
|
-
)}
|
|
9147
|
+
{/* No results message \u2014 based on the count of *visible* options. */}
|
|
9148
|
+
{searchable && effectiveSearchQuery && totalRendered === 0 && (
|
|
9149
|
+
<div className="py-6 text-center text-sm text-semantic-text-muted">
|
|
9150
|
+
No results found
|
|
9151
|
+
</div>
|
|
9152
|
+
)}
|
|
9084
9153
|
|
|
9085
9154
|
{/* Loading-more row (lazy-load) */}
|
|
9086
9155
|
{loadingMore && (
|
package/package.json
CHANGED