CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/account/config/search/component.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Input, List } from "antd";
7
import { join } from "path";
8
import { useState } from "react";
9
10
import AIAvatar from "@cocalc/frontend/components/ai-avatar";
11
import { Icon, IconName } from "@cocalc/frontend/components/icon";
12
import { capitalize } from "@cocalc/util/misc";
13
import A from "components/misc/A";
14
import { useRouter } from "next/router";
15
import register from "../register";
16
import { Info, search } from "./entries";
17
18
interface Props {
19
allowEmpty?: boolean; // if true allow empty search
20
}
21
22
export default function Search({ allowEmpty }: Props) {
23
const [value, setValue] = useState<string>("");
24
const [results, setResults] = useState<Info[]>(search(value, allowEmpty));
25
const router = useRouter();
26
27
function onSearch(value: string) {
28
setResults(search(value));
29
}
30
31
return (
32
<div>
33
<Input.Search
34
autoFocus={allowEmpty}
35
style={{ maxWidth: "60ex" }}
36
placeholder="Search all configuration options (use /re/ for regexp)..."
37
onSearch={onSearch}
38
enterButton
39
allowClear
40
value={value}
41
onChange={(e) => {
42
setValue(e.target.value);
43
onSearch(e.target.value);
44
}}
45
onPressEnter={() => {
46
if (results != null && results.length > 0) {
47
// visit first search result.
48
router.push(join("/config", results[0].path));
49
setValue("");
50
}
51
}}
52
/>
53
<br />
54
<br />
55
{results != null && (allowEmpty || value.trim()) && (
56
<SearchResults results={results} onClick={() => setValue("")} />
57
)}
58
</div>
59
);
60
}
61
62
register({
63
path: "search/input",
64
title: "Search",
65
desc: "",
66
icon: "search",
67
Component: () => <Search allowEmpty />,
68
});
69
70
function SearchResults({
71
results,
72
onClick,
73
}: {
74
results: Info[];
75
onClick: Function;
76
}) {
77
const router = useRouter();
78
79
function renderAvatar(item) {
80
if (item.icon == null) return;
81
const icon: IconName | "ai" = item.icon;
82
if (icon === "ai") {
83
return (
84
<AIAvatar
85
size={22}
86
style={{ position: "relative", top: "-2px", paddingRight: "15px" }}
87
/>
88
);
89
} else {
90
return <Icon name={item.icon} style={{ fontSize: "16pt" }} />;
91
}
92
}
93
94
return (
95
<List
96
style={{ marginBottom: "15px" }}
97
bordered
98
itemLayout="horizontal"
99
dataSource={results}
100
locale={{ emptyText: <>No results</> }}
101
renderItem={(item) => {
102
const top = item.path.split("/")[0];
103
return (
104
<A
105
title={item.title}
106
onClick={() => {
107
onClick();
108
router.push(join("/config", item.path));
109
}}
110
>
111
<List.Item style={{ borderBottom: "1px solid lightgrey" }}>
112
<List.Item.Meta
113
avatar={renderAvatar(item)}
114
title={
115
<>
116
{capitalize(top)} <Icon name="arrow-right" />{" "}
117
<span style={{ color: "darkblue" }}>{item.title}</span>
118
</>
119
}
120
description={item.desc}
121
/>
122
</List.Item>
123
</A>
124
);
125
}}
126
/>
127
);
128
}
129
130