myoperator-mcp 0.2.349 → 0.2.351
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 +606 -19
- 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
|
+
warning: "bg-semantic-warning-surface text-semantic-warning-primary",
|
|
713
714
|
failed: "bg-semantic-error-surface text-semantic-error-primary",
|
|
714
715
|
disabled: "bg-semantic-bg-ui text-semantic-text-muted",
|
|
715
716
|
default: "bg-semantic-bg-ui text-semantic-text-primary",
|
|
@@ -739,6 +740,7 @@ const badgeVariants = cva(
|
|
|
739
740
|
* @example
|
|
740
741
|
* \`\`\`tsx
|
|
741
742
|
* <Badge variant="active">Active</Badge>
|
|
743
|
+
* <Badge variant="warning">Warning</Badge>
|
|
742
744
|
* <Badge variant="failed">Failed</Badge>
|
|
743
745
|
* <Badge variant="disabled">Disabled</Badge>
|
|
744
746
|
* <Badge variant="default">Default</Badge>
|
|
@@ -1511,6 +1513,11 @@ export interface CreatableMultiSelectProps extends Omit<
|
|
|
1511
1513
|
* If the raw value differs from the sanitized value, \`onInvalidCharacters\` is called.
|
|
1512
1514
|
*/
|
|
1513
1515
|
sanitizeInput?: (raw: string) => string;
|
|
1516
|
+
/**
|
|
1517
|
+
* Applied after \`sanitizeInput\` on typed draft values (e.g. collapse spaces).
|
|
1518
|
+
* Does not affect invalid-character detection.
|
|
1519
|
+
*/
|
|
1520
|
+
normalizeInput?: (sanitized: string) => string;
|
|
1514
1521
|
/** Fired when \`sanitizeInput\` removed one or more characters from the raw input. */
|
|
1515
1522
|
onInvalidCharacters?: () => void;
|
|
1516
1523
|
/**
|
|
@@ -1600,6 +1607,12 @@ function isValueAlreadySelected(
|
|
|
1600
1607
|
});
|
|
1601
1608
|
}
|
|
1602
1609
|
|
|
1610
|
+
function restoreInputCursor(input: HTMLInputElement, cursorPosition: number) {
|
|
1611
|
+
window.requestAnimationFrame(() => {
|
|
1612
|
+
input.setSelectionRange(cursorPosition, cursorPosition);
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1603
1616
|
const CreatableMultiSelect = React.forwardRef(
|
|
1604
1617
|
(
|
|
1605
1618
|
{
|
|
@@ -1617,6 +1630,7 @@ const CreatableMultiSelect = React.forwardRef(
|
|
|
1617
1630
|
showPerItemCharacterCounter = true,
|
|
1618
1631
|
triggerDisplay = "chips",
|
|
1619
1632
|
sanitizeInput,
|
|
1633
|
+
normalizeInput,
|
|
1620
1634
|
onInvalidCharacters,
|
|
1621
1635
|
onValidInput,
|
|
1622
1636
|
onInputValueChange,
|
|
@@ -1641,6 +1655,17 @@ const CreatableMultiSelect = React.forwardRef(
|
|
|
1641
1655
|
maxLengthPerItem
|
|
1642
1656
|
);
|
|
1643
1657
|
|
|
1658
|
+
const normalizeDraftValue = React.useCallback(
|
|
1659
|
+
(raw: string) => {
|
|
1660
|
+
const sanitized = sanitizeInput ? sanitizeInput(raw) : raw;
|
|
1661
|
+
const normalized = normalizeInput ? normalizeInput(sanitized) : sanitized;
|
|
1662
|
+
return maxLengthPerItem != null
|
|
1663
|
+
? normalized.slice(0, maxLengthPerItem)
|
|
1664
|
+
: normalized;
|
|
1665
|
+
},
|
|
1666
|
+
[maxLengthPerItem, normalizeInput, sanitizeInput]
|
|
1667
|
+
);
|
|
1668
|
+
|
|
1644
1669
|
const addValue = (val: string) => {
|
|
1645
1670
|
const isPreset = options.some((o) => o.value === val);
|
|
1646
1671
|
const afterSanitize = isPreset
|
|
@@ -1648,7 +1673,9 @@ const CreatableMultiSelect = React.forwardRef(
|
|
|
1648
1673
|
: sanitizeInput
|
|
1649
1674
|
? sanitizeInput(val)
|
|
1650
1675
|
: val;
|
|
1651
|
-
const
|
|
1676
|
+
const afterNormalize =
|
|
1677
|
+
isPreset || !normalizeInput ? afterSanitize : normalizeInput(afterSanitize);
|
|
1678
|
+
const trimmed = afterNormalize.trim();
|
|
1652
1679
|
if (
|
|
1653
1680
|
!trimmed ||
|
|
1654
1681
|
isValueAlreadySelected(
|
|
@@ -1837,12 +1864,19 @@ const CreatableMultiSelect = React.forwardRef(
|
|
|
1837
1864
|
if (raw !== sanitized) onInvalidCharacters?.();
|
|
1838
1865
|
else onValidInput?.();
|
|
1839
1866
|
}
|
|
1840
|
-
const nextInput =
|
|
1841
|
-
maxLengthPerItem != null
|
|
1842
|
-
? sanitized.slice(0, maxLengthPerItem)
|
|
1843
|
-
: sanitized;
|
|
1867
|
+
const nextInput = normalizeDraftValue(raw);
|
|
1844
1868
|
setInputValue(nextInput);
|
|
1845
1869
|
onInputValueChange?.(nextInput);
|
|
1870
|
+
|
|
1871
|
+
if (nextInput !== raw) {
|
|
1872
|
+
const input = e.currentTarget;
|
|
1873
|
+
const rawCursor = input.selectionStart ?? raw.length;
|
|
1874
|
+
const nextCursor = Math.min(
|
|
1875
|
+
normalizeDraftValue(raw.slice(0, rawCursor)).length,
|
|
1876
|
+
nextInput.length
|
|
1877
|
+
);
|
|
1878
|
+
restoreInputCursor(input, nextCursor);
|
|
1879
|
+
}
|
|
1846
1880
|
}}
|
|
1847
1881
|
maxLength={maxLengthPerItem}
|
|
1848
1882
|
onKeyDown={handleKeyDown}
|
|
@@ -2104,6 +2138,12 @@ export const creatableToneHintRowClassName = cn(
|
|
|
2104
2138
|
"flex shrink-0 items-center justify-between gap-2 px-4 py-2"
|
|
2105
2139
|
)
|
|
2106
2140
|
|
|
2141
|
+
function restoreInputCursor(input: HTMLInputElement, cursorPosition: number) {
|
|
2142
|
+
window.requestAnimationFrame(() => {
|
|
2143
|
+
input.setSelectionRange(cursorPosition, cursorPosition)
|
|
2144
|
+
})
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2107
2147
|
export interface CreatableSelectOption {
|
|
2108
2148
|
value: string
|
|
2109
2149
|
label: string
|
|
@@ -2183,6 +2223,17 @@ const CreatableSelect = React.forwardRef(
|
|
|
2183
2223
|
// Merge forwarded ref with internal ref
|
|
2184
2224
|
React.useImperativeHandle(ref, () => containerRef.current!)
|
|
2185
2225
|
|
|
2226
|
+
const normalizeSearchValue = React.useCallback(
|
|
2227
|
+
(raw: string) => {
|
|
2228
|
+
const sanitized = sanitizeInput ? sanitizeInput(raw) : raw
|
|
2229
|
+
const normalized = normalizeComboboxInput
|
|
2230
|
+
? normalizeComboboxInput(sanitized)
|
|
2231
|
+
: sanitized
|
|
2232
|
+
return maxLength != null ? normalized.slice(0, maxLength) : normalized
|
|
2233
|
+
},
|
|
2234
|
+
[maxLength, normalizeComboboxInput, sanitizeInput]
|
|
2235
|
+
)
|
|
2236
|
+
|
|
2186
2237
|
const selectedLabel = React.useMemo(() => {
|
|
2187
2238
|
const found = options.find((o) => o.value === value)
|
|
2188
2239
|
return found ? found.label : value || ""
|
|
@@ -2335,13 +2386,19 @@ const CreatableSelect = React.forwardRef(
|
|
|
2335
2386
|
if (raw !== sanitized) onInvalidCharacters?.()
|
|
2336
2387
|
else onValidInput?.()
|
|
2337
2388
|
}
|
|
2338
|
-
const
|
|
2339
|
-
? normalizeComboboxInput(sanitized)
|
|
2340
|
-
: sanitized
|
|
2341
|
-
const nextSearch =
|
|
2342
|
-
maxLength != null ? next.slice(0, maxLength) : next
|
|
2389
|
+
const nextSearch = normalizeSearchValue(raw)
|
|
2343
2390
|
setSearch(nextSearch)
|
|
2344
2391
|
onInputValueChange?.(nextSearch)
|
|
2392
|
+
|
|
2393
|
+
if (nextSearch !== raw) {
|
|
2394
|
+
const input = e.currentTarget
|
|
2395
|
+
const rawCursor = input.selectionStart ?? raw.length
|
|
2396
|
+
const nextCursor = Math.min(
|
|
2397
|
+
normalizeSearchValue(raw.slice(0, rawCursor)).length,
|
|
2398
|
+
nextSearch.length
|
|
2399
|
+
)
|
|
2400
|
+
restoreInputCursor(input, nextCursor)
|
|
2401
|
+
}
|
|
2345
2402
|
}}
|
|
2346
2403
|
maxLength={maxLength}
|
|
2347
2404
|
onKeyDown={handleKeyDown}
|
|
@@ -4060,7 +4117,7 @@ const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>(
|
|
|
4060
4117
|
{weekDays.map((day) => (
|
|
4061
4118
|
<div
|
|
4062
4119
|
key={day}
|
|
4063
|
-
className="flex size-8 items-center justify-center text-xs font-
|
|
4120
|
+
className="flex size-8 items-center justify-center text-xs font-semibold text-semantic-text-muted"
|
|
4064
4121
|
>
|
|
4065
4122
|
{day}
|
|
4066
4123
|
</div>
|
|
@@ -4444,7 +4501,7 @@ const DeleteConfirmationModal = React.forwardRef(
|
|
|
4444
4501
|
<div className="grid gap-2 py-4">
|
|
4445
4502
|
<label
|
|
4446
4503
|
htmlFor="delete-confirmation-input"
|
|
4447
|
-
className="text-sm text-semantic-text-secondary"
|
|
4504
|
+
className="text-sm font-semibold text-semantic-text-secondary"
|
|
4448
4505
|
>
|
|
4449
4506
|
Enter "{confirmText}" in uppercase to confirm
|
|
4450
4507
|
</label>
|
|
@@ -5179,7 +5236,7 @@ function getCollapsedCursorPosition(value: string, cursorPosition: number) {
|
|
|
5179
5236
|
* Input variants for different visual states
|
|
5180
5237
|
*/
|
|
5181
5238
|
const inputVariants = cva(
|
|
5182
|
-
"h-[42px] w-full rounded bg-semantic-bg-primary px-4 py-2 text-base text-semantic-text-primary outline-none transition-all file:border-0 file:bg-transparent file:text-base file:font-
|
|
5239
|
+
"h-[42px] w-full rounded bg-semantic-bg-primary px-4 py-2 text-base text-semantic-text-primary outline-none transition-all file:border-0 file:bg-transparent file:text-base file:font-semibold file:text-semantic-text-primary placeholder:text-semantic-text-placeholder disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)]",
|
|
5183
5240
|
{
|
|
5184
5241
|
variants: {
|
|
5185
5242
|
state: {
|
|
@@ -5888,7 +5945,7 @@ const MultiSelect = React.forwardRef(
|
|
|
5888
5945
|
<label
|
|
5889
5946
|
htmlFor={selectId}
|
|
5890
5947
|
className={cn(
|
|
5891
|
-
"text-sm font-
|
|
5948
|
+
"text-sm font-semibold text-semantic-text-secondary",
|
|
5892
5949
|
labelClassName
|
|
5893
5950
|
)}
|
|
5894
5951
|
>
|
|
@@ -7457,7 +7514,7 @@ export const ReadableField = React.forwardRef(
|
|
|
7457
7514
|
>
|
|
7458
7515
|
{/* Header Row: Label + Optional Action */}
|
|
7459
7516
|
<div className="flex items-start justify-between">
|
|
7460
|
-
<span className="text-sm text-semantic-text-secondary tracking-[0.035px]">
|
|
7517
|
+
<span className="text-sm font-semibold text-semantic-text-secondary tracking-[0.035px]">
|
|
7461
7518
|
{label}
|
|
7462
7519
|
</span>
|
|
7463
7520
|
{headerAction && (
|
|
@@ -7709,6 +7766,517 @@ const ReplyQuote = React.forwardRef<HTMLDivElement, ReplyQuoteProps>(
|
|
|
7709
7766
|
ReplyQuote.displayName = "ReplyQuote";
|
|
7710
7767
|
|
|
7711
7768
|
export { ReplyQuote };`,
|
|
7769
|
+
"search-filter": `import * as React from "react";
|
|
7770
|
+
import { createPortal } from "react-dom";
|
|
7771
|
+
import {
|
|
7772
|
+
autoUpdate,
|
|
7773
|
+
flip,
|
|
7774
|
+
offset,
|
|
7775
|
+
shift,
|
|
7776
|
+
size as floatingSize,
|
|
7777
|
+
useFloating,
|
|
7778
|
+
} from "@floating-ui/react-dom";
|
|
7779
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
7780
|
+
import { Search, X } from "lucide-react";
|
|
7781
|
+
|
|
7782
|
+
import { cn } from "@/lib/utils";
|
|
7783
|
+
import { Button } from "./button";
|
|
7784
|
+
import { Checkbox } from "./checkbox";
|
|
7785
|
+
import { Input, type InputProps } from "./input";
|
|
7786
|
+
|
|
7787
|
+
const searchFilterVariants = cva(
|
|
7788
|
+
"relative flex min-w-0 flex-col",
|
|
7789
|
+
{
|
|
7790
|
+
variants: {
|
|
7791
|
+
size: {
|
|
7792
|
+
sm: "w-full max-w-80",
|
|
7793
|
+
default: "w-full max-w-[360px]",
|
|
7794
|
+
lg: "w-full max-w-[420px]",
|
|
7795
|
+
},
|
|
7796
|
+
},
|
|
7797
|
+
defaultVariants: {
|
|
7798
|
+
size: "default",
|
|
7799
|
+
},
|
|
7800
|
+
}
|
|
7801
|
+
);
|
|
7802
|
+
|
|
7803
|
+
const searchFilterDropdownVariants = cva(
|
|
7804
|
+
"flex flex-col overflow-hidden p-0"
|
|
7805
|
+
);
|
|
7806
|
+
|
|
7807
|
+
export type SearchFilterSearchMode = "text" | "numeric";
|
|
7808
|
+
|
|
7809
|
+
function normalizeSearchText(value: string, searchMode: SearchFilterSearchMode) {
|
|
7810
|
+
if (searchMode === "numeric") {
|
|
7811
|
+
return value.replace(/\\D/g, "");
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7814
|
+
return value.trim().toLowerCase();
|
|
7815
|
+
}
|
|
7816
|
+
|
|
7817
|
+
function renderHighlightedNumericLabel(label: string, query: string) {
|
|
7818
|
+
if (!query) return label;
|
|
7819
|
+
|
|
7820
|
+
const digitCharacterIndexes: number[] = [];
|
|
7821
|
+
const labelDigits = Array.from(label)
|
|
7822
|
+
.filter((character, index) => {
|
|
7823
|
+
const isDigit = /\\d/.test(character);
|
|
7824
|
+
if (isDigit) {
|
|
7825
|
+
digitCharacterIndexes.push(index);
|
|
7826
|
+
}
|
|
7827
|
+
return isDigit;
|
|
7828
|
+
})
|
|
7829
|
+
.join("");
|
|
7830
|
+
const matchStart = labelDigits.indexOf(query);
|
|
7831
|
+
|
|
7832
|
+
if (matchStart === -1) return label;
|
|
7833
|
+
|
|
7834
|
+
const highlightedIndexes = new Set(
|
|
7835
|
+
digitCharacterIndexes.slice(matchStart, matchStart + query.length)
|
|
7836
|
+
);
|
|
7837
|
+
|
|
7838
|
+
const chunks: Array<{ text: string; highlighted: boolean }> = [];
|
|
7839
|
+
|
|
7840
|
+
Array.from(label).forEach((character, index) => {
|
|
7841
|
+
const highlighted = highlightedIndexes.has(index);
|
|
7842
|
+
const previousChunk = chunks[chunks.length - 1];
|
|
7843
|
+
|
|
7844
|
+
if (previousChunk && previousChunk.highlighted === highlighted) {
|
|
7845
|
+
previousChunk.text += character;
|
|
7846
|
+
return;
|
|
7847
|
+
}
|
|
7848
|
+
|
|
7849
|
+
chunks.push({ text: character, highlighted });
|
|
7850
|
+
});
|
|
7851
|
+
|
|
7852
|
+
return chunks.map((chunk, index) => (
|
|
7853
|
+
<span
|
|
7854
|
+
key={\`\${chunk.text}-\${index}\`}
|
|
7855
|
+
className={chunk.highlighted ? "font-semibold" : undefined}
|
|
7856
|
+
>
|
|
7857
|
+
{chunk.text}
|
|
7858
|
+
</span>
|
|
7859
|
+
));
|
|
7860
|
+
}
|
|
7861
|
+
|
|
7862
|
+
export interface SearchFilterOption {
|
|
7863
|
+
/** Unique option value returned in selection callbacks */
|
|
7864
|
+
value: string;
|
|
7865
|
+
/** Display label shown beside the checkbox */
|
|
7866
|
+
label: string;
|
|
7867
|
+
/** Disables selection for this row */
|
|
7868
|
+
disabled?: boolean;
|
|
7869
|
+
}
|
|
7870
|
+
|
|
7871
|
+
export interface SearchFilterProps
|
|
7872
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
|
|
7873
|
+
VariantProps<typeof searchFilterVariants> {
|
|
7874
|
+
/** Options displayed in the filter list */
|
|
7875
|
+
options: SearchFilterOption[];
|
|
7876
|
+
/** Controlled search text */
|
|
7877
|
+
searchValue?: string;
|
|
7878
|
+
/** Initial search text for uncontrolled usage */
|
|
7879
|
+
defaultSearchValue?: string;
|
|
7880
|
+
/** Called whenever search text changes */
|
|
7881
|
+
onSearchChange?: (value: string) => void;
|
|
7882
|
+
/** Placeholder for the search input */
|
|
7883
|
+
searchPlaceholder?: string;
|
|
7884
|
+
/** Search input behavior. Defaults to "numeric" for phone-number filtering. */
|
|
7885
|
+
searchMode?: SearchFilterSearchMode;
|
|
7886
|
+
/** Message shown when filtering returns no options */
|
|
7887
|
+
emptyMessage?: string;
|
|
7888
|
+
/** Cancel button label */
|
|
7889
|
+
cancelLabel?: string;
|
|
7890
|
+
/** Apply button label */
|
|
7891
|
+
applyLabel?: string;
|
|
7892
|
+
/** Called when Cancel is clicked */
|
|
7893
|
+
onCancel?: () => void;
|
|
7894
|
+
/** Called with the current selection when Apply is clicked */
|
|
7895
|
+
onApply?: (value: string[]) => void;
|
|
7896
|
+
/** Disables the whole filter */
|
|
7897
|
+
disabled?: boolean;
|
|
7898
|
+
/** Disables only the search input */
|
|
7899
|
+
searchDisabled?: boolean;
|
|
7900
|
+
/** Disables the Cancel button */
|
|
7901
|
+
cancelDisabled?: boolean;
|
|
7902
|
+
/** Disables the Apply button */
|
|
7903
|
+
applyDisabled?: boolean;
|
|
7904
|
+
/** Class name for the scrollable options region */
|
|
7905
|
+
listClassName?: string;
|
|
7906
|
+
/** Additional props for the search input */
|
|
7907
|
+
inputProps?: Omit<InputProps, "value" | "defaultValue" | "type">;
|
|
7908
|
+
}
|
|
7909
|
+
|
|
7910
|
+
const SearchFilter = React.forwardRef<HTMLDivElement, SearchFilterProps>(
|
|
7911
|
+
(
|
|
7912
|
+
{
|
|
7913
|
+
className,
|
|
7914
|
+
size,
|
|
7915
|
+
options,
|
|
7916
|
+
searchValue,
|
|
7917
|
+
defaultSearchValue = "",
|
|
7918
|
+
onSearchChange,
|
|
7919
|
+
searchPlaceholder = "Search...",
|
|
7920
|
+
searchMode = "numeric",
|
|
7921
|
+
emptyMessage = "No options found",
|
|
7922
|
+
cancelLabel = "Cancel",
|
|
7923
|
+
applyLabel = "Apply",
|
|
7924
|
+
onCancel,
|
|
7925
|
+
onApply,
|
|
7926
|
+
disabled = false,
|
|
7927
|
+
searchDisabled = false,
|
|
7928
|
+
cancelDisabled = false,
|
|
7929
|
+
applyDisabled = false,
|
|
7930
|
+
listClassName,
|
|
7931
|
+
inputProps,
|
|
7932
|
+
...props
|
|
7933
|
+
},
|
|
7934
|
+
ref
|
|
7935
|
+
) => {
|
|
7936
|
+
const {
|
|
7937
|
+
className: inputClassName,
|
|
7938
|
+
onChange: inputOnChange,
|
|
7939
|
+
onClick: inputOnClick,
|
|
7940
|
+
onFocus: inputOnFocus,
|
|
7941
|
+
onKeyDown: inputOnKeyDown,
|
|
7942
|
+
inputMode: inputInputMode,
|
|
7943
|
+
pattern: inputPattern,
|
|
7944
|
+
...searchInputProps
|
|
7945
|
+
} = inputProps ?? {};
|
|
7946
|
+
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
|
|
7947
|
+
const inputId = React.useId();
|
|
7948
|
+
const dropdownId = React.useId();
|
|
7949
|
+
const optionIdPrefix = React.useId();
|
|
7950
|
+
const [selectedValues, setSelectedValues] = React.useState<string[]>([]);
|
|
7951
|
+
const [internalSearchValue, setInternalSearchValue] =
|
|
7952
|
+
React.useState(defaultSearchValue);
|
|
7953
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
7954
|
+
const currentSearchValue = searchValue ?? internalSearchValue;
|
|
7955
|
+
const displaySearchValue =
|
|
7956
|
+
searchMode === "numeric"
|
|
7957
|
+
? normalizeSearchText(currentSearchValue, searchMode)
|
|
7958
|
+
: currentSearchValue;
|
|
7959
|
+
const normalizedSearchQuery = normalizeSearchText(
|
|
7960
|
+
currentSearchValue,
|
|
7961
|
+
searchMode
|
|
7962
|
+
);
|
|
7963
|
+
|
|
7964
|
+
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
|
7965
|
+
|
|
7966
|
+
const { refs, floatingStyles } = useFloating({
|
|
7967
|
+
open: isOpen,
|
|
7968
|
+
placement: "bottom-start",
|
|
7969
|
+
strategy: "fixed",
|
|
7970
|
+
middleware: [
|
|
7971
|
+
offset(8),
|
|
7972
|
+
flip({ padding: 8 }),
|
|
7973
|
+
shift({ padding: 8 }),
|
|
7974
|
+
floatingSize({
|
|
7975
|
+
padding: 8,
|
|
7976
|
+
apply({ availableHeight, rects, elements }) {
|
|
7977
|
+
elements.floating.style.width = \`\${rects.reference.width}px\`;
|
|
7978
|
+
elements.floating.style.maxHeight = \`\${Math.min(
|
|
7979
|
+
368,
|
|
7980
|
+
availableHeight
|
|
7981
|
+
)}px\`;
|
|
7982
|
+
},
|
|
7983
|
+
}),
|
|
7984
|
+
],
|
|
7985
|
+
whileElementsMounted: (reference, floating, update) =>
|
|
7986
|
+
autoUpdate(reference, floating, update, { animationFrame: true }),
|
|
7987
|
+
});
|
|
7988
|
+
|
|
7989
|
+
const setOpen = React.useCallback(
|
|
7990
|
+
(nextOpen: boolean) => {
|
|
7991
|
+
if (disabled) return;
|
|
7992
|
+
|
|
7993
|
+
setIsOpen(nextOpen);
|
|
7994
|
+
},
|
|
7995
|
+
[disabled]
|
|
7996
|
+
);
|
|
7997
|
+
|
|
7998
|
+
const focusSearchInput = React.useCallback(() => {
|
|
7999
|
+
window.requestAnimationFrame(() => {
|
|
8000
|
+
searchInputRef.current?.focus();
|
|
8001
|
+
});
|
|
8002
|
+
}, []);
|
|
8003
|
+
|
|
8004
|
+
const setRootRef = React.useCallback(
|
|
8005
|
+
(node: HTMLDivElement | null) => {
|
|
8006
|
+
containerRef.current = node;
|
|
8007
|
+
|
|
8008
|
+
if (typeof ref === "function") {
|
|
8009
|
+
ref(node);
|
|
8010
|
+
} else if (ref) {
|
|
8011
|
+
ref.current = node;
|
|
8012
|
+
}
|
|
8013
|
+
},
|
|
8014
|
+
[ref]
|
|
8015
|
+
);
|
|
8016
|
+
|
|
8017
|
+
const setAnchorRef = React.useCallback(
|
|
8018
|
+
(node: HTMLDivElement | null) => {
|
|
8019
|
+
refs.setReference(node);
|
|
8020
|
+
},
|
|
8021
|
+
[refs]
|
|
8022
|
+
);
|
|
8023
|
+
|
|
8024
|
+
const setDropdownRef = React.useCallback(
|
|
8025
|
+
(node: HTMLDivElement | null) => {
|
|
8026
|
+
refs.setFloating(node);
|
|
8027
|
+
},
|
|
8028
|
+
[refs]
|
|
8029
|
+
);
|
|
8030
|
+
|
|
8031
|
+
React.useEffect(() => {
|
|
8032
|
+
if (!isOpen) return;
|
|
8033
|
+
|
|
8034
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
8035
|
+
const target = event.target as Node;
|
|
8036
|
+
if (
|
|
8037
|
+
containerRef.current?.contains(target) ||
|
|
8038
|
+
refs.floating.current?.contains(target)
|
|
8039
|
+
) {
|
|
8040
|
+
return;
|
|
8041
|
+
}
|
|
8042
|
+
setOpen(false);
|
|
8043
|
+
};
|
|
8044
|
+
|
|
8045
|
+
const timeoutId = window.setTimeout(() => {
|
|
8046
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
8047
|
+
}, 0);
|
|
8048
|
+
|
|
8049
|
+
return () => {
|
|
8050
|
+
window.clearTimeout(timeoutId);
|
|
8051
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
8052
|
+
};
|
|
8053
|
+
}, [isOpen, refs.floating, setOpen]);
|
|
8054
|
+
|
|
8055
|
+
const filteredOptions = React.useMemo(() => {
|
|
8056
|
+
if (!normalizedSearchQuery) return options;
|
|
8057
|
+
|
|
8058
|
+
return options.filter((option) =>
|
|
8059
|
+
normalizeSearchText(option.label, searchMode).includes(
|
|
8060
|
+
normalizedSearchQuery
|
|
8061
|
+
)
|
|
8062
|
+
);
|
|
8063
|
+
}, [normalizedSearchQuery, options, searchMode]);
|
|
8064
|
+
|
|
8065
|
+
const toggleOption = React.useCallback(
|
|
8066
|
+
(option: SearchFilterOption) => {
|
|
8067
|
+
if (disabled || option.disabled) return;
|
|
8068
|
+
|
|
8069
|
+
const isSelected = selectedValues.includes(option.value);
|
|
8070
|
+
setSelectedValues(
|
|
8071
|
+
isSelected
|
|
8072
|
+
? selectedValues.filter((item) => item !== option.value)
|
|
8073
|
+
: [...selectedValues, option.value]
|
|
8074
|
+
);
|
|
8075
|
+
},
|
|
8076
|
+
[disabled, selectedValues, setSelectedValues]
|
|
8077
|
+
);
|
|
8078
|
+
|
|
8079
|
+
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
8080
|
+
const nextSearchValue = normalizeSearchText(
|
|
8081
|
+
event.target.value,
|
|
8082
|
+
searchMode
|
|
8083
|
+
);
|
|
8084
|
+
event.target.value = nextSearchValue;
|
|
8085
|
+
|
|
8086
|
+
if (searchValue === undefined) {
|
|
8087
|
+
setInternalSearchValue(nextSearchValue);
|
|
8088
|
+
}
|
|
8089
|
+
setOpen(true);
|
|
8090
|
+
onSearchChange?.(nextSearchValue);
|
|
8091
|
+
};
|
|
8092
|
+
|
|
8093
|
+
const handleClearSearch = () => {
|
|
8094
|
+
if (searchValue === undefined) {
|
|
8095
|
+
setInternalSearchValue("");
|
|
8096
|
+
}
|
|
8097
|
+
onSearchChange?.("");
|
|
8098
|
+
setOpen(true);
|
|
8099
|
+
focusSearchInput();
|
|
8100
|
+
};
|
|
8101
|
+
|
|
8102
|
+
const handleCancel = () => {
|
|
8103
|
+
onCancel?.();
|
|
8104
|
+
setOpen(false);
|
|
8105
|
+
};
|
|
8106
|
+
|
|
8107
|
+
const handleApply = () => {
|
|
8108
|
+
onApply?.(selectedValues);
|
|
8109
|
+
setOpen(false);
|
|
8110
|
+
};
|
|
8111
|
+
|
|
8112
|
+
const dropdownPanel = (
|
|
8113
|
+
<>
|
|
8114
|
+
<div
|
|
8115
|
+
className={cn(
|
|
8116
|
+
"max-h-60 overflow-auto p-1",
|
|
8117
|
+
disabled && "pointer-events-none opacity-60",
|
|
8118
|
+
listClassName
|
|
8119
|
+
)}
|
|
8120
|
+
role="group"
|
|
8121
|
+
aria-label="Filter options"
|
|
8122
|
+
>
|
|
8123
|
+
{filteredOptions.length > 0 ? (
|
|
8124
|
+
filteredOptions.map((option, index) => {
|
|
8125
|
+
const isSelected = selectedValues.includes(option.value);
|
|
8126
|
+
const optionDisabled = disabled || option.disabled;
|
|
8127
|
+
|
|
8128
|
+
const optionLabelId = \`\${optionIdPrefix}-\${index}\`;
|
|
8129
|
+
|
|
8130
|
+
return (
|
|
8131
|
+
<div
|
|
8132
|
+
key={option.value}
|
|
8133
|
+
role="option"
|
|
8134
|
+
aria-selected={isSelected}
|
|
8135
|
+
aria-disabled={optionDisabled}
|
|
8136
|
+
className={cn(
|
|
8137
|
+
"relative flex w-full min-w-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2 text-left text-sm text-semantic-text-primary outline-none",
|
|
8138
|
+
!isSelected &&
|
|
8139
|
+
"hover:bg-semantic-bg-ui focus:bg-semantic-bg-ui",
|
|
8140
|
+
optionDisabled && "cursor-not-allowed opacity-50"
|
|
8141
|
+
)}
|
|
8142
|
+
onClick={() => toggleOption(option)}
|
|
8143
|
+
>
|
|
8144
|
+
<Checkbox
|
|
8145
|
+
size="sm"
|
|
8146
|
+
checked={isSelected}
|
|
8147
|
+
disabled={optionDisabled}
|
|
8148
|
+
aria-label={option.label}
|
|
8149
|
+
className="shrink-0"
|
|
8150
|
+
onClick={(event) => event.stopPropagation()}
|
|
8151
|
+
onCheckedChange={() => toggleOption(option)}
|
|
8152
|
+
/>
|
|
8153
|
+
<span id={optionLabelId} className="min-w-0 flex-1 truncate text-left">
|
|
8154
|
+
{searchMode === "numeric"
|
|
8155
|
+
? renderHighlightedNumericLabel(
|
|
8156
|
+
option.label,
|
|
8157
|
+
normalizedSearchQuery
|
|
8158
|
+
)
|
|
8159
|
+
: option.label}
|
|
8160
|
+
</span>
|
|
8161
|
+
</div>
|
|
8162
|
+
);
|
|
8163
|
+
})
|
|
8164
|
+
) : (
|
|
8165
|
+
<div className="px-4 py-6 text-center text-sm text-semantic-text-muted">
|
|
8166
|
+
{emptyMessage}
|
|
8167
|
+
</div>
|
|
8168
|
+
)}
|
|
8169
|
+
</div>
|
|
8170
|
+
|
|
8171
|
+
<div className="flex flex-wrap items-center justify-end gap-4 border-t border-solid border-semantic-border-layout p-4">
|
|
8172
|
+
<Button
|
|
8173
|
+
type="button"
|
|
8174
|
+
variant="outline"
|
|
8175
|
+
size="lg"
|
|
8176
|
+
className="min-w-0 px-4"
|
|
8177
|
+
disabled={disabled || cancelDisabled}
|
|
8178
|
+
onClick={handleCancel}
|
|
8179
|
+
>
|
|
8180
|
+
{cancelLabel}
|
|
8181
|
+
</Button>
|
|
8182
|
+
<Button
|
|
8183
|
+
type="button"
|
|
8184
|
+
size="lg"
|
|
8185
|
+
className="min-w-0 px-4"
|
|
8186
|
+
disabled={disabled || applyDisabled}
|
|
8187
|
+
onClick={handleApply}
|
|
8188
|
+
>
|
|
8189
|
+
{applyLabel}
|
|
8190
|
+
</Button>
|
|
8191
|
+
</div>
|
|
8192
|
+
</>
|
|
8193
|
+
);
|
|
8194
|
+
|
|
8195
|
+
return (
|
|
8196
|
+
<div
|
|
8197
|
+
ref={setRootRef}
|
|
8198
|
+
className={cn(searchFilterVariants({ size }), className)}
|
|
8199
|
+
{...props}
|
|
8200
|
+
>
|
|
8201
|
+
<div ref={setAnchorRef} className="relative">
|
|
8202
|
+
<Search className="pointer-events-none absolute left-4 top-1/2 size-4 -translate-y-1/2 text-semantic-text-muted" />
|
|
8203
|
+
<Input
|
|
8204
|
+
ref={searchInputRef}
|
|
8205
|
+
id={inputId}
|
|
8206
|
+
type="text"
|
|
8207
|
+
role="searchbox"
|
|
8208
|
+
placeholder={searchPlaceholder}
|
|
8209
|
+
value={displaySearchValue}
|
|
8210
|
+
disabled={disabled || searchDisabled}
|
|
8211
|
+
inputMode={searchMode === "numeric" ? "numeric" : inputInputMode}
|
|
8212
|
+
pattern={searchMode === "numeric" ? "[0-9]*" : inputPattern}
|
|
8213
|
+
aria-controls={dropdownId}
|
|
8214
|
+
aria-expanded={isOpen}
|
|
8215
|
+
aria-haspopup="listbox"
|
|
8216
|
+
className={cn("h-10 pl-10 pr-9", inputClassName)}
|
|
8217
|
+
{...searchInputProps}
|
|
8218
|
+
onClick={(event) => {
|
|
8219
|
+
setOpen(true);
|
|
8220
|
+
focusSearchInput();
|
|
8221
|
+
inputOnClick?.(event);
|
|
8222
|
+
}}
|
|
8223
|
+
onFocus={(event) => {
|
|
8224
|
+
setOpen(true);
|
|
8225
|
+
focusSearchInput();
|
|
8226
|
+
inputOnFocus?.(event);
|
|
8227
|
+
}}
|
|
8228
|
+
onKeyDown={(event) => {
|
|
8229
|
+
if (event.key === "Escape") {
|
|
8230
|
+
setOpen(false);
|
|
8231
|
+
}
|
|
8232
|
+
inputOnKeyDown?.(event);
|
|
8233
|
+
}}
|
|
8234
|
+
onChange={(event) => {
|
|
8235
|
+
handleSearchChange(event);
|
|
8236
|
+
inputOnChange?.(event);
|
|
8237
|
+
}}
|
|
8238
|
+
/>
|
|
8239
|
+
{displaySearchValue && !disabled && !searchDisabled ? (
|
|
8240
|
+
<button
|
|
8241
|
+
type="button"
|
|
8242
|
+
aria-label="Clear search"
|
|
8243
|
+
className="absolute right-3 top-1/2 flex size-5 -translate-y-1/2 items-center justify-center rounded text-semantic-text-muted transition-colors hover:text-semantic-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-primary focus-visible:ring-offset-1"
|
|
8244
|
+
onClick={handleClearSearch}
|
|
8245
|
+
>
|
|
8246
|
+
<X className="size-4" />
|
|
8247
|
+
</button>
|
|
8248
|
+
) : null}
|
|
8249
|
+
</div>
|
|
8250
|
+
{isOpen &&
|
|
8251
|
+
typeof document !== "undefined" &&
|
|
8252
|
+
createPortal(
|
|
8253
|
+
<div
|
|
8254
|
+
ref={setDropdownRef}
|
|
8255
|
+
id={dropdownId}
|
|
8256
|
+
className={cn(
|
|
8257
|
+
"rounded border border-solid border-semantic-border-layout bg-semantic-bg-primary text-semantic-text-primary shadow-md",
|
|
8258
|
+
searchFilterDropdownVariants()
|
|
8259
|
+
)}
|
|
8260
|
+
style={{
|
|
8261
|
+
...floatingStyles,
|
|
8262
|
+
zIndex: 10050,
|
|
8263
|
+
}}
|
|
8264
|
+
role="dialog"
|
|
8265
|
+
aria-labelledby={inputId}
|
|
8266
|
+
onMouseDown={(event) => event.stopPropagation()}
|
|
8267
|
+
>
|
|
8268
|
+
{dropdownPanel}
|
|
8269
|
+
</div>,
|
|
8270
|
+
document.body
|
|
8271
|
+
)}
|
|
8272
|
+
</div>
|
|
8273
|
+
);
|
|
8274
|
+
}
|
|
8275
|
+
);
|
|
8276
|
+
SearchFilter.displayName = "SearchFilter";
|
|
8277
|
+
|
|
8278
|
+
export { SearchFilter, searchFilterVariants };
|
|
8279
|
+
`,
|
|
7712
8280
|
"select-field": `import * as React from "react";
|
|
7713
8281
|
import { Loader2, Search } from "lucide-react";
|
|
7714
8282
|
|
|
@@ -8020,7 +8588,7 @@ const SelectField = React.forwardRef(
|
|
|
8020
8588
|
<label
|
|
8021
8589
|
htmlFor={selectId}
|
|
8022
8590
|
className={cn(
|
|
8023
|
-
"text-sm font-
|
|
8591
|
+
"text-sm font-semibold text-semantic-text-secondary",
|
|
8024
8592
|
labelClassName
|
|
8025
8593
|
)}
|
|
8026
8594
|
>
|
|
@@ -8423,7 +8991,7 @@ const SelectLabel = React.forwardRef(({ className, ...props }: React.ComponentPr
|
|
|
8423
8991
|
<SelectPrimitive.Label
|
|
8424
8992
|
ref={ref}
|
|
8425
8993
|
className={cn(
|
|
8426
|
-
"px-4 py-1.5 text-xs font-
|
|
8994
|
+
"px-4 py-1.5 text-xs font-semibold text-semantic-text-muted",
|
|
8427
8995
|
className
|
|
8428
8996
|
)}
|
|
8429
8997
|
{...props}
|
|
@@ -9742,7 +10310,7 @@ const TextField = React.forwardRef(
|
|
|
9742
10310
|
<label
|
|
9743
10311
|
htmlFor={inputId}
|
|
9744
10312
|
className={cn(
|
|
9745
|
-
"text-sm font-
|
|
10313
|
+
"text-sm font-semibold text-semantic-text-secondary",
|
|
9746
10314
|
labelClassName
|
|
9747
10315
|
)}
|
|
9748
10316
|
>
|
|
@@ -10079,7 +10647,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
10079
10647
|
<label
|
|
10080
10648
|
htmlFor={textareaId}
|
|
10081
10649
|
className={cn(
|
|
10082
|
-
"text-sm font-
|
|
10650
|
+
"text-sm font-semibold text-semantic-text-secondary",
|
|
10083
10651
|
labelClassName
|
|
10084
10652
|
)}
|
|
10085
10653
|
>
|
|
@@ -11958,6 +12526,25 @@ var componentMetadata = {
|
|
|
11958
12526
|
}
|
|
11959
12527
|
]
|
|
11960
12528
|
},
|
|
12529
|
+
"search-filter": {
|
|
12530
|
+
"name": "SearchFilter",
|
|
12531
|
+
"description": "A search filter component.",
|
|
12532
|
+
"dependencies": [
|
|
12533
|
+
"class-variance-authority",
|
|
12534
|
+
"clsx",
|
|
12535
|
+
"tailwind-merge",
|
|
12536
|
+
"lucide-react"
|
|
12537
|
+
],
|
|
12538
|
+
"props": [],
|
|
12539
|
+
"variants": [],
|
|
12540
|
+
"examples": [
|
|
12541
|
+
{
|
|
12542
|
+
"title": "Basic SearchFilter",
|
|
12543
|
+
"code": "<SearchFilter>Content</SearchFilter>",
|
|
12544
|
+
"description": "Simple search filter usage"
|
|
12545
|
+
}
|
|
12546
|
+
]
|
|
12547
|
+
},
|
|
11961
12548
|
"select-field": {
|
|
11962
12549
|
"name": "SelectField",
|
|
11963
12550
|
"description": "A form-ready select component with label, helper text, error handling, and grouped options support.",
|
package/package.json
CHANGED