@agentero/design-system 0.33.1 → 0.34.0
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/mcp/manifests/components.html +315 -95
- package/mcp/manifests/components.json +170 -13
- package/package.json +5 -1
- package/src/combobox/combobox.d.ts +12 -7
- package/src/combobox/combobox.js +79 -62
- package/src/input-search/icons.d.ts +16 -0
- package/src/input-search/icons.js +23 -0
- package/src/input-search/index.d.ts +2 -0
- package/src/input-search/index.js +2 -0
- package/src/input-search/input-search.d.ts +96 -0
- package/src/input-search/input-search.js +53 -0
|
@@ -2798,7 +2798,7 @@
|
|
|
2798
2798
|
"id": "components-combobox--default",
|
|
2799
2799
|
"name": "Default",
|
|
2800
2800
|
"snippet": "const Default = () => (\n <Combobox.Root items={STATES}>\n <Combobox.Input placeholder=\"Search states\" aria-label=\"Search states\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
|
|
2801
|
-
"description": "The common case: options are already in memory and Base UI filters them against\nwhat you type. Selecting a row fills the input and closes the list. The field is\na search out of the box — a magnifier in front, a clear button once
|
|
2801
|
+
"description": "The common case: options are already in memory and Base UI filters them against\nwhat you type. Selecting a row fills the input and closes the list. The field is\na search out of the box — a magnifier in front, a clear button once there is\ntext — and the list hangs from the whole field. Clearing drops the picked option.",
|
|
2802
2802
|
"summary": "Search field with a magnifier and clear button over an in-memory list"
|
|
2803
2803
|
},
|
|
2804
2804
|
{
|
|
@@ -2837,14 +2837,14 @@
|
|
|
2837
2837
|
{
|
|
2838
2838
|
"id": "components-combobox--async-search",
|
|
2839
2839
|
"name": "Async Search",
|
|
2840
|
-
"snippet": "const AsyncSearch = () => {\n const [query, setQuery] = useState('');\n\n return (\n // No `items` and `filter={null}`: the server did the filtering, the child renders what came\n // back, and `isItemEqualToValue` keeps a re-fetched copy counting as the selected one.\n <Combobox.Root\n filter={null}\n itemToStringLabel={(agency: Agency) => agency.name}\n isItemEqualToValue={(a: Agency, b: Agency) => a.id === b.id}\n onInputValueChange={(value, { reason }) => {\n // Picking a row writes its label into the input; that is not a new search.\n if (reason !== 'item-press') setQuery(value);\n }}>\n <Combobox.Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n <Combobox.Content>\n <Suspense fallback={<SkeletonRows />}>\n <AgencyItems query={
|
|
2841
|
-
"description": "Options fetched as you type, the way the apps load data: the rows are a component\nthat suspends on the request, so a `Suspense` boundary inside `Content` shows\nplaceholder rows until
|
|
2840
|
+
"snippet": "const AsyncSearch = () => {\n const [query, setQuery] = useState('');\n // The rows read the deferred query: while the new one suspends, React keeps rendering the\n // old rows instead of falling back to the placeholders.\n const deferredQuery = useDeferredValue(query);\n const isStale = query !== deferredQuery;\n\n return (\n // No `items` and `filter={null}`: the server did the filtering, the child renders what came\n // back, and `isItemEqualToValue` keeps a re-fetched copy counting as the selected one.\n <Combobox.Root\n filter={null}\n itemToStringLabel={(agency: Agency) => agency.name}\n isItemEqualToValue={(a: Agency, b: Agency) => a.id === b.id}\n onInputValueChange={(value, { reason }) => {\n // Picking a row writes its label into the input; that is not a new search.\n if (reason !== 'item-press') setQuery(value);\n }}>\n <Combobox.Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n <Combobox.Content aria-busy={isStale || undefined}>\n <Suspense fallback={<SkeletonRows />}>\n <AgencyItems query={deferredQuery} />\n </Suspense>\n </Combobox.Content>\n </Combobox.Root>\n );\n};",
|
|
2841
|
+
"description": "Options fetched as you type, the way the apps load data: the rows are a component\nthat suspends on the request, so a `Suspense` boundary inside `Content` shows\nplaceholder rows until the first results land. The rows read a deferred copy of\nthe query, so a new search keeps the previous rows on screen while it loads and\n`Content` is marked busy until they are replaced; the placeholders never come\nback. Nothing tracks `loading` by hand, and reading the data by query is what\nmakes out-of-order responses harmless. With react-query, drop a\n`useSuspenseQuery` where `use()` is, or use `placeholderData: keepPreviousData`\nwith a plain `useQuery` for the same behaviour."
|
|
2842
2842
|
},
|
|
2843
2843
|
{
|
|
2844
2844
|
"id": "components-combobox--async-search-with-status",
|
|
2845
2845
|
"name": "Async Search With Status",
|
|
2846
|
-
"snippet": "const AsyncSearchWithStatus = () => {\n const [results, setResults] = useState<Agency[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n // Only the latest request may land; fast typing returns responses out of order.\n const latestRequest = useRef(0);\n\n const onInputValueChange = async (query: string) => {\n const request = ++latestRequest.current;\n if (query.trim().length < MIN_QUERY_LENGTH) {\n setResults([]);\n setIsLoading(false);\n return;\n }\n\n // The previous rows
|
|
2847
|
-
"description": "The same search without Suspense, for a consumer that gets a `loading` flag from\nits data layer instead: `filter={null}`, the rows arrive through `filteredItems`,\n`Content` is marked busy and `Status` announces the wait.
|
|
2846
|
+
"snippet": "const AsyncSearchWithStatus = () => {\n const [results, setResults] = useState<Agency[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n // Only the latest request may land; fast typing returns responses out of order.\n const latestRequest = useRef(0);\n\n const onInputValueChange = async (query: string) => {\n const request = ++latestRequest.current;\n if (query.trim().length < MIN_QUERY_LENGTH) {\n setResults([]);\n setIsLoading(false);\n return;\n }\n\n // The previous rows stay through the wait, marked busy, rather than blinking out and back.\n setIsLoading(true);\n const agencies = await searchAgencies(query);\n if (request !== latestRequest.current) return;\n\n setResults(agencies);\n setIsLoading(false);\n };\n\n return (\n // `filter={null}` hands filtering to the server; `filteredItems` is what came back.\n <Combobox.Root\n items={results}\n filteredItems={results}\n filter={null}\n minInputLength={MIN_QUERY_LENGTH}\n itemToStringLabel={(agency: Agency) => agency.name}\n isItemEqualToValue={(a: Agency, b: Agency) => a.id === b.id}\n onInputValueChange={onInputValueChange}>\n <Combobox.Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n <Combobox.Content aria-busy={isLoading || undefined}>\n {/* The rows show the wait; `Status` only announces it. */}\n <Combobox.Status className=\"sr-only\">\n {isLoading ? 'Searching…' : undefined}\n </Combobox.Status>\n {isLoading && results.length === 0 && <SkeletonRows />}\n <Combobox.Empty>{isLoading ? undefined : 'No agencies found'}</Combobox.Empty>\n <Combobox.List>\n {(agency: Agency) => (\n <Combobox.Item key={agency.id} value={agency}>\n {agency.name}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n );\n};",
|
|
2847
|
+
"description": "The same search without Suspense, for a consumer that gets a `loading` flag from\nits data layer instead: `filter={null}`, the rows arrive through `filteredItems`,\n`Content` is marked busy and `Status` announces the wait. Placeholder rows show\nonly while there is nothing to show yet; after that, the previous rows stay\nuntil the new ones replace them. A request that is no longer the latest is\ndropped.\n\n`minInputLength` keeps the list shut until two characters are typed — focusing\nthe field opens nothing, and deleting back under the minimum closes it — and\nno request goes out for a shorter query either.",
|
|
2848
2848
|
"summary": "API search that waits for two characters, with loading status"
|
|
2849
2849
|
},
|
|
2850
2850
|
{
|
|
@@ -6552,7 +6552,7 @@
|
|
|
6552
6552
|
"id": "components-inputgroup--default",
|
|
6553
6553
|
"name": "Default",
|
|
6554
6554
|
"snippet": "const Default = () => (\n <InputGroup.Root>\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <Input type=\"search\" placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n </InputGroup.Root>\n);",
|
|
6555
|
-
"description": "A search field: the icon leads and the input takes the rest of the frame.\nPressing the icon focuses the input, and the frame — not the input — draws the\nfocus ring, so the two never show a double border.",
|
|
6555
|
+
"description": "A search field: the icon leads and the input takes the rest of the frame.\nPressing the icon focuses the input, and the frame — not the input — draws the\nfocus ring, so the two never show a double border. A search field with a\nclear button and a loading state is\n[InputSearch](?path=/docs/components-inputsearch--docs), built on this frame.",
|
|
6556
6556
|
"summary": "Leading icon addon with the frame drawing the focus ring"
|
|
6557
6557
|
},
|
|
6558
6558
|
{
|
|
@@ -6609,11 +6609,6 @@
|
|
|
6609
6609
|
"description": "Inside a `FieldText` nothing changes for the group: the `Input` reads the\nfield's context at any depth, so the label names it and the description\ndescribes it with no ids written by hand. The frame is not in the way.",
|
|
6610
6610
|
"summary": "Label and description reaching the input through the frame"
|
|
6611
6611
|
},
|
|
6612
|
-
{
|
|
6613
|
-
"id": "components-inputgroup--search-field",
|
|
6614
|
-
"name": "Search Field",
|
|
6615
|
-
"snippet": "const SearchField = () => (\n <form onSubmit={handleSearchSubmit}>\n <FieldStates\n label=\"Agency\"\n tooltip=\"Search by agency name or NPN.\"\n required\n error=\"Pick an agency from the list.\">\n <SearchInput />\n </FieldStates>\n </form>\n);"
|
|
6616
|
-
},
|
|
6617
6612
|
{
|
|
6618
6613
|
"id": "components-inputgroup--email-field",
|
|
6619
6614
|
"name": "Email Field",
|
|
@@ -6636,7 +6631,7 @@
|
|
|
6636
6631
|
"summary": "Phone field with a leading country code picker, valid and invalid"
|
|
6637
6632
|
}
|
|
6638
6633
|
],
|
|
6639
|
-
"import": "import {\n Button,\n Divider,\n DropdownMenu,\n Field,\n FieldText,\n
|
|
6634
|
+
"import": "import {\n Button,\n Divider,\n DropdownMenu,\n Field,\n FieldText,\n IconKeyboardArrowDown,\n IconSearch,\n Input,\n InputGroup,\n Label,\n Tag,\n} from \"@agentero/design-system\";"
|
|
6640
6635
|
},
|
|
6641
6636
|
"components-inputpassword": {
|
|
6642
6637
|
"id": "components-inputpassword",
|
|
@@ -6744,6 +6739,168 @@
|
|
|
6744
6739
|
],
|
|
6745
6740
|
"import": "import { Field, FieldText, InputPassword, Label } from \"@agentero/design-system\";"
|
|
6746
6741
|
},
|
|
6742
|
+
"components-inputsearch": {
|
|
6743
|
+
"id": "components-inputsearch",
|
|
6744
|
+
"name": "InputSearch",
|
|
6745
|
+
"path": "./src/input-search/input-search.stories.tsx",
|
|
6746
|
+
"jsDocTags": {
|
|
6747
|
+
"summary": [
|
|
6748
|
+
"Search input with a leading icon, a clear button and a loading slot"
|
|
6749
|
+
],
|
|
6750
|
+
"dataAttribute": [
|
|
6751
|
+
"{string} data-slot - Always set to \"input-search\" on the frame"
|
|
6752
|
+
],
|
|
6753
|
+
"example": [
|
|
6754
|
+
"<InputSearch\n aria-label=\"Search agencies\"\n value={query}\n onChange={event => setQuery(event.target.value)}\n isLoading={isFetching}\n/>"
|
|
6755
|
+
]
|
|
6756
|
+
},
|
|
6757
|
+
"description": "InputSearch is a search field with a magnifier in front and a clear button\nonce there is text. It is an `Input` of `type=\"search\"` inside an\n`InputGroup`: `className` styles the frame and every other prop reaches the\n`<input>`. Clearing reports a real change event with an empty value, and\n`isLoading` swaps the clear button for a spinner.",
|
|
6758
|
+
"summary": "Search input with a leading icon, a clear button and a loading slot",
|
|
6759
|
+
"reactComponentMeta": {
|
|
6760
|
+
"displayName": "InputSearch",
|
|
6761
|
+
"exportName": "InputSearch",
|
|
6762
|
+
"filePath": "/home/runner/work/design-system/design-system/src/input-search/input-search.tsx",
|
|
6763
|
+
"description": "InputSearch is a search field: a magnifier in front of the text and, once\nthere is text, a clear button after it, named \"Clear search\" unless\n`clearLabel` says otherwise. The clear button empties the field\nand puts focus back in it, and it reports the change through `onChange` like\na keystroke would: a real change event whose `event.target.value` is `''`,\nso a controlled field, an uncontrolled one and one registered with\nreact-hook-form all clear the same way. While `isLoading` is set, a spinner\ntakes the clear button's place. The clear button is not rendered while the\ninput is disabled, whether `disabled` comes from the prop, from a\nsurrounding `FieldText` or from a `<fieldset disabled>`.\n\nIt is an [Input](?path=/docs/components-input--docs) of `type=\"search\"`\ninside an [InputGroup](?path=/docs/components-inputgroup--docs) frame, so\nit is announced as a search box. `className` styles the frame. Every other\nprop goes to the `<input>`: the `ref`, `id`, `name`, `value`, `onChange`,\n`aria-*` and every other standard attribute. Only `type` is not accepted.\n`placeholder` defaults to `'Search'`. Inside a `FieldText` the input takes\nits label, messages and states from the field as a plain `Input` does. The\ninvalid state comes from `aria-invalid`, and the frame paints the\ndestructive border.\n\nDo not use it to pick a value from a list; that is\n[Combobox](?path=/docs/components-combobox--docs), whose `search` field is\ndrawn by InputSearch. For a filter that should\nrun only once the user pauses typing, use `DebouncedInputSearch`.",
|
|
6764
|
+
"jsDocTags": {
|
|
6765
|
+
"summary": [
|
|
6766
|
+
"Search input with a leading icon, a clear button and a loading slot"
|
|
6767
|
+
],
|
|
6768
|
+
"dataAttribute": [
|
|
6769
|
+
"{string} data-slot - Always set to \"input-search\" on the frame"
|
|
6770
|
+
],
|
|
6771
|
+
"example": [
|
|
6772
|
+
"<InputSearch\n aria-label=\"Search agencies\"\n value={query}\n onChange={event => setQuery(event.target.value)}\n isLoading={isFetching}\n/>"
|
|
6773
|
+
]
|
|
6774
|
+
},
|
|
6775
|
+
"props": {
|
|
6776
|
+
"size": {
|
|
6777
|
+
"name": "size",
|
|
6778
|
+
"required": false,
|
|
6779
|
+
"type": {
|
|
6780
|
+
"name": "enum",
|
|
6781
|
+
"raw": "\"sm\" | \"md\" | \"lg\"",
|
|
6782
|
+
"value": [
|
|
6783
|
+
{
|
|
6784
|
+
"value": "\"sm\""
|
|
6785
|
+
},
|
|
6786
|
+
{
|
|
6787
|
+
"value": "\"md\""
|
|
6788
|
+
},
|
|
6789
|
+
{
|
|
6790
|
+
"value": "\"lg\""
|
|
6791
|
+
}
|
|
6792
|
+
]
|
|
6793
|
+
},
|
|
6794
|
+
"description": "Control height. Defaults to `'md'`.\n- `sm` (32px) — dense layouts where vertical space is tight.\n- `md` (40px) — standard form density.\n- `lg` (48px) — low-density forms and larger touch targets. Also raises\n the text to `base` and the corner radius to `lg`.",
|
|
6795
|
+
"defaultValue": null,
|
|
6796
|
+
"parent": {
|
|
6797
|
+
"name": "InputProps",
|
|
6798
|
+
"fileName": "src/input/input.tsx"
|
|
6799
|
+
},
|
|
6800
|
+
"declarations": [
|
|
6801
|
+
{
|
|
6802
|
+
"name": "TypeLiteral",
|
|
6803
|
+
"fileName": "src/input/input.tsx"
|
|
6804
|
+
}
|
|
6805
|
+
]
|
|
6806
|
+
},
|
|
6807
|
+
"isLoading": {
|
|
6808
|
+
"name": "isLoading",
|
|
6809
|
+
"required": false,
|
|
6810
|
+
"type": {
|
|
6811
|
+
"name": "boolean"
|
|
6812
|
+
},
|
|
6813
|
+
"description": "Shows a spinner at the end of the frame, in place of the clear button,\nwhile the results for the current text are on their way. Defaults to\n`false`.",
|
|
6814
|
+
"defaultValue": {
|
|
6815
|
+
"value": "false"
|
|
6816
|
+
},
|
|
6817
|
+
"parent": {
|
|
6818
|
+
"name": "InputSearchProps",
|
|
6819
|
+
"fileName": "src/input-search/input-search.tsx"
|
|
6820
|
+
},
|
|
6821
|
+
"declarations": [
|
|
6822
|
+
{
|
|
6823
|
+
"name": "TypeLiteral",
|
|
6824
|
+
"fileName": "src/input-search/input-search.tsx"
|
|
6825
|
+
}
|
|
6826
|
+
]
|
|
6827
|
+
},
|
|
6828
|
+
"clearLabel": {
|
|
6829
|
+
"name": "clearLabel",
|
|
6830
|
+
"required": false,
|
|
6831
|
+
"type": {
|
|
6832
|
+
"name": "string"
|
|
6833
|
+
},
|
|
6834
|
+
"description": "Accessible name of the clear button. Defaults to `'Clear search'`; name it\nafter what is searched (`'Clear agency search'`) when a page carries\nseveral search fields.",
|
|
6835
|
+
"defaultValue": {
|
|
6836
|
+
"value": "'Clear search'"
|
|
6837
|
+
},
|
|
6838
|
+
"parent": {
|
|
6839
|
+
"name": "InputSearchProps",
|
|
6840
|
+
"fileName": "src/input-search/input-search.tsx"
|
|
6841
|
+
},
|
|
6842
|
+
"declarations": [
|
|
6843
|
+
{
|
|
6844
|
+
"name": "TypeLiteral",
|
|
6845
|
+
"fileName": "src/input-search/input-search.tsx"
|
|
6846
|
+
}
|
|
6847
|
+
]
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
},
|
|
6851
|
+
"stories": [
|
|
6852
|
+
{
|
|
6853
|
+
"id": "components-inputsearch--default",
|
|
6854
|
+
"name": "Default",
|
|
6855
|
+
"snippet": "const Default = () => {\n const [query, setQuery] = useState('');\n\n return (\n <InputSearch\n aria-label=\"Search agencies\"\n value={query}\n onChange={event => {\n setQuery(event.target.value);\n args.onChange?.(event);\n }} />\n );\n};",
|
|
6856
|
+
"description": "A controlled search. The clear button appears once there is text; pressing\nit calls `onChange` with an empty `event.target.value`, so the state that\nowns the query empties too, and focus goes back to the input. The browser's\nown cancel button is hidden.",
|
|
6857
|
+
"summary": "Controlled search with a clear button that empties the query"
|
|
6858
|
+
},
|
|
6859
|
+
{
|
|
6860
|
+
"id": "components-inputsearch--loading",
|
|
6861
|
+
"name": "Loading",
|
|
6862
|
+
"snippet": "const Loading = () => <InputSearch\n aria-label=\"Search agencies\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n isLoading\n defaultValue=\"Acme\" />;",
|
|
6863
|
+
"description": "While `isLoading` is set a spinner takes the clear button's place, even with\ntext in the field.",
|
|
6864
|
+
"summary": "Spinner in place of the clear button while results load"
|
|
6865
|
+
},
|
|
6866
|
+
{
|
|
6867
|
+
"id": "components-inputsearch--uncontrolled",
|
|
6868
|
+
"name": "Uncontrolled",
|
|
6869
|
+
"snippet": "const Uncontrolled = () => <InputSearch\n aria-label=\"Search agencies\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n defaultValue=\"Acme Insurance\" />;",
|
|
6870
|
+
"description": "An uncontrolled search with a `defaultValue`. The clear button follows what\nis in the field, and clearing still calls `onChange` with an empty value.",
|
|
6871
|
+
"summary": "Uncontrolled search starting from a default value"
|
|
6872
|
+
},
|
|
6873
|
+
{
|
|
6874
|
+
"id": "components-inputsearch--disabled",
|
|
6875
|
+
"name": "Disabled",
|
|
6876
|
+
"snippet": "const Disabled = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputSearch\n aria-label=\"Search agencies\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n disabled\n defaultValue=\"Acme\" />\n <fieldset disabled style={{ border: 0, margin: 0, padding: 0 }}>\n <InputSearch\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n aria-label=\"Search carriers\"\n defaultValue=\"Travelers\" />\n </fieldset>\n</div>;",
|
|
6877
|
+
"description": "A disabled search has no clear button, whether `disabled` is set on the\ninput itself or comes from a `<fieldset disabled>` around it.",
|
|
6878
|
+
"summary": "Disabled search with the clear button removed"
|
|
6879
|
+
},
|
|
6880
|
+
{
|
|
6881
|
+
"id": "components-inputsearch--all-sizes",
|
|
6882
|
+
"name": "All Sizes",
|
|
6883
|
+
"snippet": "const AllSizes = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputSearch\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"sm\"\n aria-label=\"Small\"\n defaultValue=\"Acme\" />\n <InputSearch\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"md\"\n aria-label=\"Medium\"\n defaultValue=\"Acme\" />\n <InputSearch\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"lg\"\n aria-label=\"Large\"\n defaultValue=\"Acme\" />\n</div>;",
|
|
6884
|
+
"description": "The three sizes match Input's: the frame is 32, 40 or 48px tall.",
|
|
6885
|
+
"summary": "The three sizes side by side"
|
|
6886
|
+
},
|
|
6887
|
+
{
|
|
6888
|
+
"id": "components-inputsearch--inside-field-text",
|
|
6889
|
+
"name": "Inside Field Text",
|
|
6890
|
+
"snippet": "const InsideFieldText = () => (\n <div style={{ display: 'grid', gap: '1.5rem' }}>\n <FieldText>\n <Label>Agency</Label>\n <InputSearch defaultValue=\"Acme\" />\n <Field.Description>Search by agency name or NPN.</Field.Description>\n </FieldText>\n <FieldText disabled>\n <Label>Carrier</Label>\n <InputSearch defaultValue=\"Travelers\" />\n </FieldText>\n </div>\n);",
|
|
6891
|
+
"description": "Inside a `FieldText` the input takes its label, description and states from\nthe field through the frame. A disabled field removes the clear button too.",
|
|
6892
|
+
"summary": "Label and disabled state reaching the input through the frame"
|
|
6893
|
+
},
|
|
6894
|
+
{
|
|
6895
|
+
"id": "components-inputsearch--clear-label",
|
|
6896
|
+
"name": "Clear Label",
|
|
6897
|
+
"snippet": "const ClearLabel = () => <InputSearch\n aria-label=\"Search agencies\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n defaultValue=\"Acme\"\n clearLabel=\"Clear agency search\" />;",
|
|
6898
|
+
"description": "`clearLabel` names the clear button after what is searched, so a page with\nseveral search fields does not read \"Clear search\" twice.",
|
|
6899
|
+
"summary": "Clear button named after what the field searches"
|
|
6900
|
+
}
|
|
6901
|
+
],
|
|
6902
|
+
"import": "import { Field, FieldText, InputSearch, Label } from \"@agentero/design-system\";"
|
|
6903
|
+
},
|
|
6747
6904
|
"components-input": {
|
|
6748
6905
|
"id": "components-input",
|
|
6749
6906
|
"name": "Input",
|
|
@@ -9710,6 +9867,6 @@
|
|
|
9710
9867
|
},
|
|
9711
9868
|
"meta": {
|
|
9712
9869
|
"docgen": "react-component-meta",
|
|
9713
|
-
"durationMs":
|
|
9870
|
+
"durationMs": 3754
|
|
9714
9871
|
}
|
|
9715
9872
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentero/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "A React component library built with Tailwind CSS v4 and Radix UI primitives",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -101,6 +101,10 @@
|
|
|
101
101
|
"types": "./src/input-password/index.d.ts",
|
|
102
102
|
"import": "./src/input-password/index.js"
|
|
103
103
|
},
|
|
104
|
+
"./input-search": {
|
|
105
|
+
"types": "./src/input-search/index.d.ts",
|
|
106
|
+
"import": "./src/input-search/index.js"
|
|
107
|
+
},
|
|
104
108
|
"./label": {
|
|
105
109
|
"types": "./src/label/index.d.ts",
|
|
106
110
|
"import": "./src/label/index.js"
|
|
@@ -145,7 +145,8 @@ type InputProps = Omit<ComponentPropsWithRef<typeof ComboboxPrimitive.Input>, 's
|
|
|
145
145
|
*/
|
|
146
146
|
variant?: 'search' | 'plain' | 'select';
|
|
147
147
|
/**
|
|
148
|
-
* Whether a clear button follows the input once
|
|
148
|
+
* Whether a clear button follows the input: on `search` once there is text,
|
|
149
|
+
* on the others once an option is picked.
|
|
149
150
|
* Defaults to `true` for `search` and `false` for `plain` and `select`; pass
|
|
150
151
|
* it to override the variant. On a `select` it replaces the chevron while
|
|
151
152
|
* there is a value. Ignored under a `multiple` root, which never
|
|
@@ -185,11 +186,13 @@ type InputProps = Omit<ComponentPropsWithRef<typeof ComboboxPrimitive.Input>, 's
|
|
|
185
186
|
* the user may want to browse before typing.
|
|
186
187
|
*
|
|
187
188
|
* Only `search` carries a clear button by default; `showClear` adds it to the
|
|
188
|
-
* other two or drops it from `search`.
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
189
|
+
* other two or drops it from `search`. The `search` field is an
|
|
190
|
+
* [InputSearch](?path=/docs/components-inputsearch--docs): its clear button
|
|
191
|
+
* shows as soon as there is text, typed or picked, and pressing it empties the
|
|
192
|
+
* text, which drops the picked option and closes the list. On a `select`, the
|
|
193
|
+
* clear button takes the chevron's place once an option is picked; the list
|
|
194
|
+
* still opens from the input. Name it with `clearLabel`, and the chevron with
|
|
195
|
+
* `triggerLabel`, when a page has more than one combobox.
|
|
193
196
|
*
|
|
194
197
|
* Under a `multiple` root there is neither: the list stays open between picks,
|
|
195
198
|
* so a chevron has nothing to toggle, and Base UI's clear button would drop the
|
|
@@ -198,7 +201,9 @@ type InputProps = Omit<ComponentPropsWithRef<typeof ComboboxPrimitive.Input>, 's
|
|
|
198
201
|
* remove each entry.
|
|
199
202
|
*
|
|
200
203
|
* Every native input attribute is accepted and forwarded to the `<input>` —
|
|
201
|
-
* `placeholder`, `disabled`, `autoComplete`
|
|
204
|
+
* `placeholder`, `disabled`, `autoComplete`. `className` styles the `<input>`
|
|
205
|
+
* too, except on the `search` field with a clear button, where it styles the
|
|
206
|
+
* frame, as on InputSearch.
|
|
202
207
|
*
|
|
203
208
|
* @summary Search, plain or select field that opens and filters the list
|
|
204
209
|
* @dataAttribute {string} data-slot - Always set to "combobox-input"
|
package/src/combobox/combobox.js
CHANGED
|
@@ -5,13 +5,14 @@ import { useFieldContext as n } from "../field/context.js";
|
|
|
5
5
|
import { InputContext as r } from "../input/context.js";
|
|
6
6
|
import { inputRecipe as i } from "../input/input.js";
|
|
7
7
|
import { InputGroup as a } from "../input-group/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { InputSearch as o } from "../input-search/input-search.js";
|
|
9
|
+
import { IconCancel as s, IconCheck as c, IconKeyboardArrowDown as l, IconSearch as u } from "./icons.js";
|
|
10
|
+
import { tv as d } from "tailwind-variants";
|
|
11
|
+
import { createContext as f, use as p, useRef as m, useState as h } from "react";
|
|
12
|
+
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
13
|
+
import { Combobox as v } from "@base-ui/react/combobox";
|
|
13
14
|
//#region src/combobox/combobox.tsx
|
|
14
|
-
var
|
|
15
|
+
var y = d({ slots: {
|
|
15
16
|
action: [
|
|
16
17
|
"flex size-6 shrink-0 cursor-pointer items-center justify-center self-center rounded-sm",
|
|
17
18
|
"[&>svg>path]:fill-icon-input-default hover:[&>svg>path]:fill-icon-default-base-primary",
|
|
@@ -47,78 +48,94 @@ var v = u({ slots: {
|
|
|
47
48
|
"group-data-disabled/item:data-selected:bg-bg-checkbox-disabled"
|
|
48
49
|
],
|
|
49
50
|
message: ["mx-2 my-px px-3 py-2 text-sm text-text-default-base-tertiary", "empty:m-0 empty:p-0"]
|
|
50
|
-
} }),
|
|
51
|
-
let s = !!o.multiple, [c, l] =
|
|
51
|
+
} }), b = y(), x = f(!1), S = ({ minInputLength: e = 0, open: t, defaultOpen: n = !1, onOpenChange: r, onOpenChangeComplete: i, onInputValueChange: a, ...o }) => {
|
|
52
|
+
let s = !!o.multiple, [c, l] = h(n), u = m(String(o.inputValue ?? o.defaultInputValue ?? ""));
|
|
52
53
|
o.inputValue !== void 0 && (u.current = String(o.inputValue));
|
|
53
|
-
let d = (t) => t.trim().length < e, f =
|
|
54
|
+
let d = (t) => t.trim().length < e, f = m(!1), p = m(!1), _ = (e, t) => {
|
|
54
55
|
if (e && d(u.current)) {
|
|
55
56
|
t.cancel();
|
|
56
57
|
return;
|
|
57
58
|
}
|
|
58
59
|
f.current = !1, r?.(e, t), s && !e && t.reason === "item-press" && t.cancel(), t.isCanceled || l(e);
|
|
59
60
|
}, y = (e) => {
|
|
60
|
-
i?.(e), !e && f.current && (f.current = !1,
|
|
61
|
-
},
|
|
62
|
-
if (
|
|
63
|
-
|
|
61
|
+
i?.(e), !e && f.current && (f.current = !1, p.current = !0, queueMicrotask(() => p.current = !1));
|
|
62
|
+
}, b = (e, n) => {
|
|
63
|
+
if (p.current) {
|
|
64
|
+
p.current = !1, n.cancel();
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
u.current = e, a?.(e, n), d(e) && t === void 0 && c && (f.current = !0, l(!1));
|
|
67
68
|
}, S = t ?? (e > 0 ? c : void 0);
|
|
68
|
-
return /* @__PURE__ */
|
|
69
|
+
return /* @__PURE__ */ g(x, {
|
|
69
70
|
value: s,
|
|
70
|
-
children: /* @__PURE__ */
|
|
71
|
+
children: /* @__PURE__ */ g(v.Root, {
|
|
71
72
|
open: S,
|
|
72
73
|
defaultOpen: n,
|
|
73
|
-
onOpenChange:
|
|
74
|
+
onOpenChange: _,
|
|
74
75
|
onOpenChangeComplete: y,
|
|
75
|
-
onInputValueChange:
|
|
76
|
+
onInputValueChange: b,
|
|
76
77
|
...o
|
|
77
78
|
})
|
|
78
79
|
});
|
|
79
80
|
};
|
|
80
|
-
|
|
81
|
-
var
|
|
81
|
+
S.displayName = "Combobox.Root";
|
|
82
|
+
var C = /* @__PURE__ */ new Set([
|
|
82
83
|
"value",
|
|
83
84
|
"defaultValue",
|
|
84
85
|
"onChange"
|
|
85
|
-
]),
|
|
86
|
-
let i =
|
|
86
|
+
]), w = (e) => {
|
|
87
|
+
let i = p(r), a = n(), o = i ? Object.fromEntries(Object.entries(i).filter(([e]) => !C.has(e))) : null, s = !("aria-label" in e || "aria-labelledby" in e) && a ? { "aria-labelledby": a.labelId } : null;
|
|
87
88
|
return t({
|
|
88
89
|
...o,
|
|
89
90
|
...s
|
|
90
91
|
}, e);
|
|
91
|
-
},
|
|
92
|
-
let { className:
|
|
93
|
-
return /* @__PURE__ */ g(
|
|
94
|
-
render: /* @__PURE__ */
|
|
92
|
+
}, T = ({ variant: t = "search", showClear: n = t === "search", clearLabel: c = "Clear", triggerLabel: d = "Show options", ...f }) => {
|
|
93
|
+
let { className: m, size: h = "md", ...y } = w(f), S = p(x);
|
|
94
|
+
return t === "search" && n && !S ? /* @__PURE__ */ g(v.InputGroup, {
|
|
95
|
+
render: /* @__PURE__ */ g("div", {}),
|
|
96
|
+
"data-slot": "combobox-field",
|
|
97
|
+
children: /* @__PURE__ */ g(r, {
|
|
98
|
+
value: null,
|
|
99
|
+
children: /* @__PURE__ */ g(v.Input, {
|
|
100
|
+
"data-slot": "combobox-input",
|
|
101
|
+
className: m,
|
|
102
|
+
...y,
|
|
103
|
+
render: (e) => /* @__PURE__ */ g(o, {
|
|
104
|
+
...e,
|
|
105
|
+
size: h,
|
|
106
|
+
clearLabel: c
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
}) : /* @__PURE__ */ _(v.InputGroup, {
|
|
111
|
+
render: /* @__PURE__ */ g(a.Root, {}),
|
|
95
112
|
"data-slot": "combobox-field",
|
|
96
113
|
className: t === "plain" ? "px-4" : void 0,
|
|
97
114
|
children: [
|
|
98
|
-
t === "search" && /* @__PURE__ */
|
|
99
|
-
/* @__PURE__ */
|
|
115
|
+
t === "search" && /* @__PURE__ */ g(a.Addon, { children: /* @__PURE__ */ g(u, {}) }),
|
|
116
|
+
/* @__PURE__ */ g(v.Input, {
|
|
100
117
|
"data-slot": "combobox-input",
|
|
101
|
-
"data-size":
|
|
102
|
-
className: e(i({ size:
|
|
103
|
-
...
|
|
118
|
+
"data-size": h,
|
|
119
|
+
className: e(i({ size: h }), m),
|
|
120
|
+
...y
|
|
104
121
|
}),
|
|
105
|
-
n && !
|
|
122
|
+
n && !S && /* @__PURE__ */ g(v.Clear, {
|
|
106
123
|
"data-slot": "combobox-clear",
|
|
107
|
-
"aria-label":
|
|
108
|
-
className:
|
|
109
|
-
children: /* @__PURE__ */
|
|
124
|
+
"aria-label": c,
|
|
125
|
+
className: b.action(),
|
|
126
|
+
children: /* @__PURE__ */ g(s, {})
|
|
110
127
|
}),
|
|
111
|
-
t === "select" && !
|
|
128
|
+
t === "select" && !S && /* @__PURE__ */ g(v.Trigger, {
|
|
112
129
|
"data-slot": "combobox-trigger",
|
|
113
|
-
"aria-label":
|
|
114
|
-
className: e(
|
|
115
|
-
children: /* @__PURE__ */
|
|
130
|
+
"aria-label": d,
|
|
131
|
+
className: e(b.action(), b.trigger(), n && b.triggerReplaced()),
|
|
132
|
+
children: /* @__PURE__ */ g(l, {})
|
|
116
133
|
})
|
|
117
134
|
]
|
|
118
135
|
});
|
|
119
136
|
};
|
|
120
|
-
|
|
121
|
-
var
|
|
137
|
+
T.displayName = "Combobox.Input";
|
|
138
|
+
var E = ({ className: t, side: n = "bottom", align: r = "start", sideOffset: i = 12, alignOffset: a, anchor: o, collisionPadding: s = 8, ...c }) => /* @__PURE__ */ g(v.Portal, { children: /* @__PURE__ */ g(v.Positioner, {
|
|
122
139
|
side: n,
|
|
123
140
|
align: r,
|
|
124
141
|
sideOffset: i,
|
|
@@ -126,49 +143,49 @@ var T = ({ className: t, side: n = "bottom", align: r = "start", sideOffset: i =
|
|
|
126
143
|
anchor: o,
|
|
127
144
|
collisionPadding: s,
|
|
128
145
|
className: "isolate z-(--z-index-top-layer) pointer-events-auto",
|
|
129
|
-
children: /* @__PURE__ */
|
|
146
|
+
children: /* @__PURE__ */ g(v.Popup, {
|
|
130
147
|
"data-slot": "combobox-content",
|
|
131
|
-
className: e(
|
|
148
|
+
className: e(b.content(), t),
|
|
132
149
|
...c
|
|
133
150
|
})
|
|
134
151
|
}) });
|
|
135
|
-
|
|
136
|
-
var
|
|
152
|
+
E.displayName = "Combobox.Content";
|
|
153
|
+
var D = ({ className: t, ...n }) => {
|
|
137
154
|
let r = "aria-label" in n || "aria-labelledby" in n;
|
|
138
|
-
return /* @__PURE__ */
|
|
155
|
+
return /* @__PURE__ */ g(v.List, {
|
|
139
156
|
"data-slot": "combobox-list",
|
|
140
157
|
"aria-label": r ? void 0 : "Suggestions",
|
|
141
|
-
className: e(
|
|
158
|
+
className: e(b.list(), t),
|
|
142
159
|
...n
|
|
143
160
|
});
|
|
144
161
|
};
|
|
145
|
-
|
|
146
|
-
var
|
|
147
|
-
let i =
|
|
148
|
-
return /* @__PURE__ */
|
|
162
|
+
D.displayName = "Combobox.List";
|
|
163
|
+
var O = ({ className: t, children: n, ...r }) => {
|
|
164
|
+
let i = p(x);
|
|
165
|
+
return /* @__PURE__ */ _(v.Item, {
|
|
149
166
|
"data-slot": "combobox-item",
|
|
150
|
-
className: e(
|
|
167
|
+
className: e(b.item(), t),
|
|
151
168
|
...r,
|
|
152
|
-
children: [i && /* @__PURE__ */
|
|
169
|
+
children: [i && /* @__PURE__ */ g(v.ItemIndicator, {
|
|
153
170
|
keepMounted: !0,
|
|
154
171
|
"data-slot": "combobox-item-indicator",
|
|
155
|
-
className:
|
|
156
|
-
children: /* @__PURE__ */
|
|
172
|
+
className: b.indicator(),
|
|
173
|
+
children: /* @__PURE__ */ g(c, { className: "size-3.5" })
|
|
157
174
|
}), n]
|
|
158
175
|
});
|
|
159
176
|
};
|
|
160
|
-
|
|
161
|
-
var
|
|
177
|
+
O.displayName = "Combobox.Item";
|
|
178
|
+
var k = ({ className: t, ...n }) => /* @__PURE__ */ g(v.Empty, {
|
|
162
179
|
"data-slot": "combobox-empty",
|
|
163
|
-
className: e(
|
|
180
|
+
className: e(b.message(), t),
|
|
164
181
|
...n
|
|
165
182
|
});
|
|
166
|
-
|
|
167
|
-
var
|
|
183
|
+
k.displayName = "Combobox.Empty";
|
|
184
|
+
var A = ({ className: t, ...n }) => /* @__PURE__ */ g(v.Status, {
|
|
168
185
|
"data-slot": "combobox-status",
|
|
169
|
-
className: e(
|
|
186
|
+
className: e(b.message(), t),
|
|
170
187
|
...n
|
|
171
188
|
});
|
|
172
|
-
|
|
189
|
+
A.displayName = "Combobox.Status";
|
|
173
190
|
//#endregion
|
|
174
|
-
export {
|
|
191
|
+
export { E as Content, k as Empty, T as Input, O as Item, D as List, S as Root, A as Status, y as comboboxRecipe };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SVGProps } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Magnifier leading InputSearch, the same glyph `Combobox` uses. Fill color
|
|
4
|
+
* comes from the input group addon, so the icon follows the field's state.
|
|
5
|
+
*
|
|
6
|
+
* @summary 24px search icon leading the InputSearch field
|
|
7
|
+
*/
|
|
8
|
+
export declare const IconSearch: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* Circled cross on the InputSearch clear button, the same glyph `Combobox`
|
|
11
|
+
* uses. Fill color comes from the button's class, so the icon follows its
|
|
12
|
+
* hover and disabled states.
|
|
13
|
+
*
|
|
14
|
+
* @summary 24px cancel icon on the InputSearch clear button
|
|
15
|
+
*/
|
|
16
|
+
export declare const IconCancel: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
+
//#region src/input-search/icons.tsx
|
|
3
|
+
var t = (t) => /* @__PURE__ */ e("svg", {
|
|
4
|
+
width: "24",
|
|
5
|
+
height: "24",
|
|
6
|
+
viewBox: "0 0 24 24",
|
|
7
|
+
fill: "none",
|
|
8
|
+
"aria-hidden": "true",
|
|
9
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
10
|
+
...t,
|
|
11
|
+
children: /* @__PURE__ */ e("path", { d: "M9.52 15.615q-2.562 0-4.34-1.777Q3.405 12.062 3.405 9.5T5.18 5.162q1.776-1.777 4.338-1.777t4.339 1.777q1.777 1.776 1.777 4.338 0 1.071-.36 2.046a5.7 5.7 0 0 1-.96 1.696l5.754 5.754q.209.209.213.522a.7.7 0 0 1-.213.532.72.72 0 0 1-.527.217.72.72 0 0 1-.527-.217l-5.753-5.754q-.75.62-1.725.97t-2.018.35m0-1.5q1.933 0 3.273-1.341 1.342-1.34 1.342-3.274 0-1.932-1.342-3.274-1.34-1.341-3.274-1.341-1.932 0-3.274 1.34Q4.904 7.569 4.904 9.5q0 1.933 1.341 3.274 1.342 1.341 3.274 1.341" })
|
|
12
|
+
}), n = (t) => /* @__PURE__ */ e("svg", {
|
|
13
|
+
width: "24",
|
|
14
|
+
height: "24",
|
|
15
|
+
viewBox: "0 0 24 24",
|
|
16
|
+
fill: "none",
|
|
17
|
+
"aria-hidden": "true",
|
|
18
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
19
|
+
...t,
|
|
20
|
+
children: /* @__PURE__ */ e("path", { d: "M12 13.0538L15.0731 16.1269C15.2115 16.2654 15.3856 16.3362 15.5952 16.3394C15.8048 16.3426 15.982 16.2718 16.1269 16.1269C16.2718 15.982 16.3442 15.8064 16.3442 15.6C16.3442 15.3936 16.2718 15.218 16.1269 15.0731L13.0538 12L16.1269 8.9269C16.2653 8.78845 16.3362 8.61442 16.3394 8.4048C16.3426 8.1952 16.2718 8.01797 16.1269 7.8731C15.982 7.72822 15.8064 7.65577 15.6 7.65577C15.3936 7.65577 15.2179 7.72822 15.0731 7.8731L12 10.9462L8.92688 7.8731C8.78842 7.73463 8.61439 7.6638 8.40478 7.6606C8.19518 7.65738 8.01794 7.72822 7.87308 7.8731C7.72819 8.01797 7.65575 8.1936 7.65575 8.4C7.65575 8.6064 7.72819 8.78203 7.87308 8.9269L10.9461 12L7.87308 15.0731C7.73461 15.2115 7.66378 15.3856 7.66058 15.5952C7.65736 15.8048 7.72819 15.982 7.87308 16.1269C8.01794 16.2718 8.19358 16.3442 8.39998 16.3442C8.60638 16.3442 8.78201 16.2718 8.92688 16.1269L12 13.0538ZM12.0016 21.5C10.6877 21.5 9.45268 21.2506 8.29655 20.752C7.1404 20.2533 6.13472 19.5766 5.2795 18.7217C4.42427 17.8669 3.74721 16.8616 3.24833 15.706C2.74944 14.5504 2.5 13.3156 2.5 12.0017C2.5 10.6877 2.74933 9.45271 3.248 8.29657C3.74667 7.14042 4.42342 6.13474 5.27825 5.27952C6.1331 4.42429 7.13834 3.74723 8.29398 3.24835C9.44959 2.74947 10.6844 2.50002 11.9983 2.50002C13.3122 2.50002 14.5473 2.74936 15.7034 3.24802C16.8596 3.74669 17.8652 4.42344 18.7205 5.27827C19.5757 6.13312 20.2527 7.13837 20.7516 8.294C21.2505 9.44962 21.5 10.6844 21.5 11.9983C21.5 13.3123 21.2506 14.5473 20.752 15.7034C20.2533 16.8596 19.5765 17.8653 18.7217 18.7205C17.8669 19.5757 16.8616 20.2528 15.706 20.7516C14.5504 21.2505 13.3156 21.5 12.0016 21.5ZM12 20C14.2333 20 16.125 19.225 17.675 17.675C19.225 16.125 20 14.2333 20 12C20 9.76667 19.225 7.875 17.675 6.325C16.125 4.775 14.2333 4 12 4C9.76664 4 7.87498 4.775 6.32498 6.325C4.77498 7.875 3.99998 9.76667 3.99998 12C3.99998 14.2333 4.77498 16.125 6.32498 17.675C7.87498 19.225 9.76664 20 12 20Z" })
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
export { n as IconCancel, t as IconSearch };
|