Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbda68b1

Browse files
authored
feat: add /icons page (#10093)
1 parent236e84c commitbda68b1

File tree

5 files changed

+226
-12
lines changed

5 files changed

+226
-12
lines changed

‎site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"ts-prune":"0.10.3",
9292
"tzdata":"1.0.30",
9393
"ua-parser-js":"1.0.33",
94+
"ufuzzy":"npm:@leeoniya/ufuzzy@1.0.10",
9495
"unique-names-generator":"4.7.1",
9596
"uuid":"9.0.0",
9697
"vite":"4.4.2",

‎site/pnpm-lock.yaml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎site/src/AppRouter.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ const TemplateInsightsPage = lazy(
193193
);
194194
constHealthPage=lazy(()=>import("./pages/HealthPage/HealthPage"));
195195
constGroupsPage=lazy(()=>import("./pages/GroupsPage/GroupsPage"));
196+
constIconsPage=lazy(()=>import("./pages/IconsPage/IconsPage"));
196197

197198
exportconstAppRouter:FC=()=>{
198199
return(
@@ -207,21 +208,21 @@ export const AppRouter: FC = () => {
207208
<Routeelement={<DashboardLayout/>}>
208209
<Routeindexelement={<Navigateto="/workspaces"replace/>}/>
209210

210-
<Routepath="health"element={<HealthPage/>}/>
211+
<Routepath="/health"element={<HealthPage/>}/>
211212

212213
<Route
213-
path="external-auth/:provider"
214+
path="/external-auth/:provider"
214215
element={<ExternalAuthPage/>}
215216
/>
216217

217-
<Routepath="workspaces"element={<WorkspacesPage/>}/>
218+
<Routepath="/workspaces"element={<WorkspacesPage/>}/>
218219

219-
<Routepath="starter-templates">
220+
<Routepath="/starter-templates">
220221
<Routeindexelement={<StarterTemplatesPage/>}/>
221222
<Routepath=":exampleId"element={<StarterTemplatePage/>}/>
222223
</Route>
223224

224-
<Routepath="templates">
225+
<Routepath="/templates">
225226
<Routeindexelement={<TemplatesPage/>}/>
226227
<Routepath="new"element={<CreateTemplatePage/>}/>
227228
<Routepath=":template">
@@ -261,7 +262,7 @@ export const AppRouter: FC = () => {
261262
</Route>
262263
</Route>
263264

264-
<Routepath="users">
265+
<Routepath="/users">
265266
<Routeelement={<UsersLayout/>}>
266267
<Routeindexelement={<UsersPage/>}/>
267268
</Route>
@@ -302,7 +303,7 @@ export const AppRouter: FC = () => {
302303
/>
303304
</Route>
304305

305-
<Routepath="settings"element={<SettingsLayout/>}>
306+
<Routepath="/settings"element={<SettingsLayout/>}>
306307
<Routepath="account"element={<AccountPage/>}/>
307308
<Routepath="schedule"element={<SchedulePage/>}/>
308309
<Routepath="security"element={<SecurityPage/>}/>
@@ -340,7 +341,8 @@ export const AppRouter: FC = () => {
340341
path="/:username/:workspace/terminal"
341342
element={<TerminalPagerenderer="webgl"/>}
342343
/>
343-
<Routepath="cli-auth"element={<CliAuthenticationPage/>}/>
344+
<Routepath="/cli-auth"element={<CliAuthenticationPage/>}/>
345+
<Routepath="/icons"element={<IconsPage/>}/>
344346
</Route>
345347

346348
{/* Using path="*"" means "match anything", so this route

‎site/src/components/CopyableValue/CopyableValue.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
1-
importTooltipfrom"@mui/material/Tooltip";
1+
importTooltip,{typeTooltipProps}from"@mui/material/Tooltip";
22
import{useClickable}from"hooks/useClickable";
33
import{useClipboard}from"hooks/useClipboard";
4-
import{FC,HTMLProps}from"react";
4+
import{typeFC,typeHTMLProps}from"react";
55

66
interfaceCopyableValuePropsextendsHTMLProps<HTMLDivElement>{
77
value:string;
8+
placement?:TooltipProps["placement"];
9+
PopperProps?:TooltipProps["PopperProps"];
810
}
911

10-
exportconstCopyableValue:FC<CopyableValueProps>=({ value, ...props})=>{
12+
exportconstCopyableValue:FC<CopyableValueProps>=({
13+
value,
14+
placement="bottom-start",
15+
PopperProps,
16+
...props
17+
})=>{
1118
const{ isCopied, copy}=useClipboard(value);
1219
constclickableProps=useClickable<HTMLSpanElement>(copy);
1320

1421
return(
1522
<Tooltip
1623
title={isCopied ?"Copied!" :"Click to copy"}
17-
placement="bottom-start"
24+
placement={placement}
25+
PopperProps={PopperProps}
1826
>
1927
<span{...props}{...clickableProps}css={{cursor:"pointer"}}/>
2028
</Tooltip>
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
importTextFieldfrom"@mui/material/TextField";
2+
importInputAdornmentfrom"@mui/material/InputAdornment";
3+
importTooltipfrom"@mui/material/Tooltip";
4+
importIconButtonfrom"@mui/material/IconButton";
5+
importBoxfrom"@mui/material/Box";
6+
importLinkfrom"@mui/material/Link";
7+
importSearchIconfrom"@mui/icons-material/SearchOutlined";
8+
importClearIconfrom"@mui/icons-material/CloseOutlined";
9+
import{useTheme}from"@emotion/react";
10+
import{typeFC,typeReactNode,useMemo,useState}from"react";
11+
import{Helmet}from"react-helmet-async";
12+
importuFuzzyfrom"ufuzzy";
13+
import{CopyableValue}from"components/CopyableValue/CopyableValue";
14+
import{EmptyState}from"components/EmptyState/EmptyState";
15+
import{Margins}from"components/Margins/Margins";
16+
import{
17+
PageHeader,
18+
PageHeaderSubtitle,
19+
PageHeaderTitle,
20+
}from"components/PageHeader/PageHeader";
21+
import{Stack}from"components/Stack/Stack";
22+
importiconsfrom"theme/icons.json";
23+
import{pageTitle}from"utils/page";
24+
25+
consticonsWithoutSuffix=icons.map((icon)=>icon.split(".")[0]);
26+
constfuzzyFinder=newuFuzzy({
27+
intraMode:1,
28+
intraIns:1,
29+
intraSub:1,
30+
intraTrn:1,
31+
intraDel:1,
32+
});
33+
34+
exportconstIconsPage:FC=()=>{
35+
consttheme=useTheme();
36+
const[searchInputText,setSearchInputText]=useState("");
37+
constsearchText=searchInputText.trim();
38+
39+
constsearchedIcons=useMemo(()=>{
40+
if(!searchText){
41+
returnicons.map((icon)=>({url:`/icon/${icon}`,description:icon}));
42+
}
43+
44+
const[map,info,sorted]=fuzzyFinder.search(
45+
iconsWithoutSuffix,
46+
searchText,
47+
);
48+
49+
// We hit an invalid state somehow
50+
if(!map||!info||!sorted){
51+
return[];
52+
}
53+
54+
returnsorted.map((i)=>{
55+
consticonName=icons[info.idx[i]];
56+
constranges=info.ranges[i];
57+
58+
constnodes:ReactNode[]=[];
59+
letcursor=0;
60+
for(letj=0;j<ranges.length;j+=2){
61+
nodes.push(iconName.slice(cursor,ranges[j]));
62+
nodes.push(
63+
<markkey={j+1}>{iconName.slice(ranges[j],ranges[j+1])}</mark>,
64+
);
65+
cursor=ranges[j+1];
66+
}
67+
nodes.push(iconName.slice(cursor));
68+
return{url:`/icon/${iconName}`,description:nodes};
69+
});
70+
},[searchText]);
71+
72+
return(
73+
<>
74+
<Helmet>
75+
<title>{pageTitle("Icons")}</title>
76+
</Helmet>
77+
<Margins>
78+
<PageHeader
79+
actions={
80+
<Tooltip
81+
placement="bottom-end"
82+
title={
83+
<Box
84+
css={{
85+
padding:theme.spacing(1),
86+
fontSize:13,
87+
lineHeight:1.5,
88+
}}
89+
>
90+
You can suggest a new icon by submitting a Pull Request to our
91+
public GitHub repository. Just keep in mind that it should be
92+
relevant to many Coder users, and redistributable under a
93+
permissive license.
94+
</Box>
95+
}
96+
>
97+
<Linkhref="https://github.com/coder/coder/tree/main/site/static/icon">
98+
Suggest an icon
99+
</Link>
100+
</Tooltip>
101+
}
102+
>
103+
<PageHeaderTitle>Icons</PageHeaderTitle>
104+
<PageHeaderSubtitle>
105+
All of the icons included with Coder
106+
</PageHeaderSubtitle>
107+
</PageHeader>
108+
<TextField
109+
size="small"
110+
InputProps={{
111+
"aria-label":"Filter",
112+
name:"query",
113+
placeholder:"Search…",
114+
value:searchInputText,
115+
onChange:(event)=>setSearchInputText(event.target.value),
116+
sx:{
117+
borderRadius:"6px",
118+
marginLeft:"-1px",
119+
"& input::placeholder":{
120+
color:theme.palette.text.secondary,
121+
},
122+
"& .MuiInputAdornment-root":{
123+
marginLeft:0,
124+
},
125+
},
126+
startAdornment:(
127+
<InputAdornmentposition="start">
128+
<SearchIcon
129+
sx={{
130+
fontSize:14,
131+
color:theme.palette.text.secondary,
132+
}}
133+
/>
134+
</InputAdornment>
135+
),
136+
endAdornment:searchInputText&&(
137+
<InputAdornmentposition="end">
138+
<Tooltiptitle="Clear filter">
139+
<IconButton
140+
size="small"
141+
onClick={()=>setSearchInputText("")}
142+
>
143+
<ClearIconsx={{fontSize:14}}/>
144+
</IconButton>
145+
</Tooltip>
146+
</InputAdornment>
147+
),
148+
}}
149+
/>
150+
151+
<Stack
152+
direction="row"
153+
wrap="wrap"
154+
spacing={1}
155+
justifyContent="center"
156+
css={(theme)=>({marginTop:theme.spacing(4)})}
157+
>
158+
{searchedIcons.length===0&&(
159+
<EmptyStatemessage="No results matched your search"/>
160+
)}
161+
{searchedIcons.map((icon)=>(
162+
<CopyableValuekey={icon.url}value={icon.url}placement="bottom">
163+
<StackalignItems="center"css={{margin:theme.spacing(1.5)}}>
164+
<img
165+
alt={icon.url}
166+
src={icon.url}
167+
css={{
168+
width:60,
169+
height:60,
170+
objectFit:"contain",
171+
pointerEvents:"none",
172+
padding:theme.spacing(1.5),
173+
}}
174+
/>
175+
<figcaption
176+
css={{
177+
width:88,
178+
height:48,
179+
fontSize:13,
180+
textOverflow:"ellipsis",
181+
textAlign:"center",
182+
overflow:"hidden",
183+
}}
184+
>
185+
{icon.description}
186+
</figcaption>
187+
</Stack>
188+
</CopyableValue>
189+
))}
190+
</Stack>
191+
</Margins>
192+
</>
193+
);
194+
};
195+
196+
exportdefaultIconsPage;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp