Skip to content

Commit 3e1a31f

Browse files
committed
Add a filter to the New Data Source modal
add a filter to make it easier to find the data source you're looking for in the New Data Source modal found in both the lower pane when creating an app, and the Data Source page when in Home. The filtering ignores case and diacritics, but is not a fuzzy filter as I thought that would be too confusing for a simple search like this. The localeContains function comes from StackOverflow user Jan Turoň, https://stackoverflow.com/a/69623589/1394698 The function sacrifices complete compatibility with the localeCompare function for a simplified diacritics check which should suffice for the majority of lowcoder use cases. I initially tried to get the npm library locale-includes working, but Vite really doesn't like it, and it failed to compile no matter what I tried.
1 parent f830bd5 commit 3e1a31f

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

client/packages/lowcoder/src/pages/datasource/pluginPanel.tsx

+49
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
apiPluginsForQueryLibrary,
1212
databasePlugins,
1313
} from "@lowcoder-ee/constants/datasourceConstants";
14+
import { Search } from "components/Search";
15+
import { CreateDropdown } from "@lowcoder-ee/pages/ApplicationV2/CreateDropdown";
16+
import React, { useState } from "react";
1417

1518
export const DataSourceButton = styled(AntdButton)`
1619
&&& {
@@ -66,17 +69,62 @@ const SectionBody = styled.div`
6669
gap: 8px;
6770
`;
6871

72+
const OperationRightWrapper = styled.div`
73+
display: flex;
74+
align-items: center;
75+
flex-shrink: 0;
76+
margin-left: auto;
77+
@media screen and (max-width: 500px) {
78+
> Button {
79+
display: none;
80+
}
81+
}
82+
`;
83+
84+
/**
85+
* Function source: https://stackoverflow.com/a/69623589/1394698, thanks to Jan Turoň
86+
*
87+
* Stripping diacritics and natively comparing the strings is much faster than using localeCompare.
88+
* localeCompare also fails to search partials, it only searches the full string match, so is quite
89+
* useless for a filter box.
90+
*
91+
* This method sacrifices some of the benefits of localeCompare, such as κόσμε == kosme and instead
92+
* focuses solely on diacritics, which should be fine for the general use case.
93+
*
94+
* @param str the full string to search against (for this panel, it's always the Data Source #name)
95+
* @param sub the filter string to search with
96+
*/
97+
export const localeContains = (str: string, sub: string): boolean => {
98+
if (sub === "") return true;
99+
if (!sub || !str.length) return false;
100+
sub = "" + sub;
101+
if (sub.length > str.length) return false;
102+
let ascii = (s: string) => s.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
103+
return ascii(str).includes(ascii(sub));
104+
}
105+
69106
export const PluginPanel = (props: { onSelect: (t: DataSourceTypeInfo) => void }) => {
70107
const datasourceTypes = useSelector(getDataSourceTypes);
71108
const currentPage = useCurrentPage();
109+
const [searchValue, setSearchValue] = useState("");
72110
const apiList = currentPage === "queryLibrary" ? apiPluginsForQueryLibrary : apiPlugins;
111+
73112
return (
74113
<PanelWrapper>
114+
<OperationRightWrapper>
115+
<Search
116+
placeholder={trans("search")}
117+
value={searchValue}
118+
onChange={(e) => setSearchValue(e.target.value)}
119+
style={{ width: "192px", height: "32px", margin: "0" }}
120+
/>
121+
</OperationRightWrapper>
75122
<SectionWrapper>
76123
<SectionLabel>{trans("query.database")}</SectionLabel>
77124
<SectionBody>
78125
{datasourceTypes
79126
.filter((t) => databasePlugins.includes(t.id) || t.definition?.category === "database")
127+
.filter((t) => localeContains(t.name, searchValue))
80128
.map((t) => {
81129
return (
82130
<DataSourceButton key={t.id} onClick={() => props.onSelect(t)}>
@@ -92,6 +140,7 @@ export const PluginPanel = (props: { onSelect: (t: DataSourceTypeInfo) => void }
92140
<SectionBody>
93141
{datasourceTypes
94142
.filter((t) => apiList.includes(t.id) || t.definition?.category === "api")
143+
.filter((t) => localeContains(t.name, searchValue))
95144
.map((t) => (
96145
<DataSourceButton key={t.id} onClick={() => props.onSelect(t)}>
97146
{t.id && getBottomResIcon(t.id, "large", t.definition?.icon)}

0 commit comments

Comments
 (0)