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

feat: add /icons page#10093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
aslilac merged 2 commits intomainfromicons-page
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletionssite/package.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,7 @@
"ts-prune": "0.10.3",
"tzdata": "1.0.30",
"ua-parser-js": "1.0.33",
"ufuzzy": "npm:@leeoniya/ufuzzy@1.0.10",
"unique-names-generator": "4.7.1",
"uuid": "9.0.0",
"vite": "4.4.2",
Expand Down
7 changes: 7 additions & 0 deletionssite/pnpm-lock.yaml
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

18 changes: 10 additions & 8 deletionssite/src/AppRouter.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ const TemplateInsightsPage = lazy(
);
const HealthPage = lazy(() => import("./pages/HealthPage/HealthPage"));
const GroupsPage = lazy(() => import("./pages/GroupsPage/GroupsPage"));
const IconsPage = lazy(() => import("./pages/IconsPage/IconsPage"));

export const AppRouter: FC = () => {
return (
Expand All@@ -207,21 +208,21 @@ export const AppRouter: FC = () => {
<Route element={<DashboardLayout />}>
<Route index element={<Navigate to="/workspaces" replace />} />

<Route path="health" element={<HealthPage />} />
<Route path="/health" element={<HealthPage />} />

<Route
path="external-auth/:provider"
path="/external-auth/:provider"
element={<ExternalAuthPage />}
/>

<Route path="workspaces" element={<WorkspacesPage />} />
<Route path="/workspaces" element={<WorkspacesPage />} />

<Route path="starter-templates">
<Route path="/starter-templates">
<Route index element={<StarterTemplatesPage />} />
<Route path=":exampleId" element={<StarterTemplatePage />} />
</Route>

<Route path="templates">
<Route path="/templates">
<Route index element={<TemplatesPage />} />
<Route path="new" element={<CreateTemplatePage />} />
<Route path=":template">
Expand DownExpand Up@@ -261,7 +262,7 @@ export const AppRouter: FC = () => {
</Route>
</Route>

<Route path="users">
<Route path="/users">
<Route element={<UsersLayout />}>
<Route index element={<UsersPage />} />
</Route>
Expand DownExpand Up@@ -302,7 +303,7 @@ export const AppRouter: FC = () => {
/>
</Route>

<Route path="settings" element={<SettingsLayout />}>
<Route path="/settings" element={<SettingsLayout />}>
<Route path="account" element={<AccountPage />} />
<Route path="schedule" element={<SchedulePage />} />
<Route path="security" element={<SecurityPage />} />
Expand DownExpand Up@@ -340,7 +341,8 @@ export const AppRouter: FC = () => {
path="/:username/:workspace/terminal"
element={<TerminalPage renderer="webgl" />}
/>
<Route path="cli-auth" element={<CliAuthenticationPage />} />
<Route path="/cli-auth" element={<CliAuthenticationPage />} />
<Route path="/icons" element={<IconsPage />} />
</Route>

{/* Using path="*"" means "match anything", so this route
Expand Down
16 changes: 12 additions & 4 deletionssite/src/components/CopyableValue/CopyableValue.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
import Tooltip from "@mui/material/Tooltip";
import Tooltip, { type TooltipProps } from "@mui/material/Tooltip";
import { useClickable } from "hooks/useClickable";
import { useClipboard } from "hooks/useClipboard";
import { FC, HTMLProps } from "react";
import {typeFC, type HTMLProps } from "react";

interface CopyableValueProps extends HTMLProps<HTMLDivElement> {
value: string;
placement?: TooltipProps["placement"];
PopperProps?: TooltipProps["PopperProps"];
}

export const CopyableValue: FC<CopyableValueProps> = ({ value, ...props }) => {
export const CopyableValue: FC<CopyableValueProps> = ({
value,
placement = "bottom-start",
PopperProps,
...props
}) => {
const { isCopied, copy } = useClipboard(value);
const clickableProps = useClickable<HTMLSpanElement>(copy);

return (
<Tooltip
title={isCopied ? "Copied!" : "Click to copy"}
placement="bottom-start"
placement={placement}
PopperProps={PopperProps}
>
<span {...props} {...clickableProps} css={{ cursor: "pointer" }} />
</Tooltip>
Expand Down
196 changes: 196 additions & 0 deletionssite/src/pages/IconsPage/IconsPage.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
import Box from "@mui/material/Box";
import Link from "@mui/material/Link";
import SearchIcon from "@mui/icons-material/SearchOutlined";
import ClearIcon from "@mui/icons-material/CloseOutlined";
import { useTheme } from "@emotion/react";
import { type FC, type ReactNode, useMemo, useState } from "react";
import { Helmet } from "react-helmet-async";
import uFuzzy from "ufuzzy";
import { CopyableValue } from "components/CopyableValue/CopyableValue";
import { EmptyState } from "components/EmptyState/EmptyState";
import { Margins } from "components/Margins/Margins";
import {
PageHeader,
PageHeaderSubtitle,
PageHeaderTitle,
} from "components/PageHeader/PageHeader";
import { Stack } from "components/Stack/Stack";
import icons from "theme/icons.json";
import { pageTitle } from "utils/page";

const iconsWithoutSuffix = icons.map((icon) => icon.split(".")[0]);
const fuzzyFinder = new uFuzzy({
intraMode: 1,
intraIns: 1,
intraSub: 1,
intraTrn: 1,
intraDel: 1,
});

export const IconsPage: FC = () => {
const theme = useTheme();
const [searchInputText, setSearchInputText] = useState("");
const searchText = searchInputText.trim();

const searchedIcons = useMemo(() => {
if (!searchText) {
return icons.map((icon) => ({ url: `/icon/${icon}`, description: icon }));
}

const [map, info, sorted] = fuzzyFinder.search(
iconsWithoutSuffix,
searchText,
);

// We hit an invalid state somehow
if (!map || !info || !sorted) {
return [];
}

return sorted.map((i) => {
const iconName = icons[info.idx[i]];
const ranges = info.ranges[i];

const nodes: ReactNode[] = [];
let cursor = 0;
for (let j = 0; j < ranges.length; j += 2) {
nodes.push(iconName.slice(cursor, ranges[j]));
nodes.push(
<mark key={j + 1}>{iconName.slice(ranges[j], ranges[j + 1])}</mark>,
);
cursor = ranges[j + 1];
}
nodes.push(iconName.slice(cursor));
return { url: `/icon/${iconName}`, description: nodes };
});
}, [searchText]);

return (
<>
<Helmet>
<title>{pageTitle("Icons")}</title>
</Helmet>
<Margins>
<PageHeader
actions={
<Tooltip
placement="bottom-end"
title={
<Box
css={{
padding: theme.spacing(1),
fontSize: 13,
lineHeight: 1.5,
}}
>
You can suggest a new icon by submitting a Pull Request to our
public GitHub repository. Just keep in mind that it should be
relevant to many Coder users, and redistributable under a
permissive license.
</Box>
}
>
<Link href="https://github.com/coder/coder/tree/main/site/static/icon">
Suggest an icon
</Link>
</Tooltip>
}
>
<PageHeaderTitle>Icons</PageHeaderTitle>
<PageHeaderSubtitle>
All of the icons included with Coder
</PageHeaderSubtitle>
</PageHeader>
<TextField
size="small"
InputProps={{
"aria-label": "Filter",
name: "query",
placeholder: "Search…",
value: searchInputText,
onChange: (event) => setSearchInputText(event.target.value),
sx: {
borderRadius: "6px",
marginLeft: "-1px",
"& input::placeholder": {
color: theme.palette.text.secondary,
},
"& .MuiInputAdornment-root": {
marginLeft: 0,
},
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon
sx={{
fontSize: 14,
color: theme.palette.text.secondary,
}}
/>
</InputAdornment>
),
endAdornment: searchInputText && (
<InputAdornment position="end">
<Tooltip title="Clear filter">
<IconButton
size="small"
onClick={() => setSearchInputText("")}
>
<ClearIcon sx={{ fontSize: 14 }} />
</IconButton>
</Tooltip>
</InputAdornment>
),
}}
/>

<Stack
direction="row"
wrap="wrap"
spacing={1}
justifyContent="center"
css={(theme) => ({ marginTop: theme.spacing(4) })}
>
{searchedIcons.length === 0 && (
<EmptyState message="No results matched your search" />
)}
{searchedIcons.map((icon) => (
<CopyableValue key={icon.url} value={icon.url} placement="bottom">
<Stack alignItems="center" css={{ margin: theme.spacing(1.5) }}>
<img
alt={icon.url}
src={icon.url}
css={{
width: 60,
height: 60,
objectFit: "contain",
pointerEvents: "none",
padding: theme.spacing(1.5),
}}
/>
<figcaption
css={{
width: 88,
height: 48,
fontSize: 13,
textOverflow: "ellipsis",
textAlign: "center",
overflow: "hidden",
}}
>
{icon.description}
</figcaption>
</Stack>
</CopyableValue>
))}
</Stack>
</Margins>
</>
);
};

export default IconsPage;

[8]ページ先頭

©2009-2025 Movatter.jp