Skip to content

Commit 54b8e79

Browse files
feat: Add emoji picker for template icons (#3601)
1 parent a4c90c5 commit 54b8e79

File tree

3,697 files changed

+128
-34
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

3,697 files changed

+128
-34
lines changed

.github/workflows/typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ MacOS = "macOS"
99
[files]
1010
extend-exclude = [
1111
"**.svg",
12+
"**.png",
1213
"**.lock",
1314
"go.sum",
1415
"go.mod",

site/can-ndjson-stream.d.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

site/emoji-mart.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
declare module "@emoji-mart/react" {
2+
const Picker: React.FC<{
3+
theme: "dark" | "light"
4+
data: Record<string, unknown>
5+
onEmojiSelect: (emojiData: { unified: string }) => void
6+
}>
7+
8+
export default Picker
9+
}

site/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@
5757
"xterm-addon-web-links": "0.6.0",
5858
"xterm-addon-webgl": "0.11.4",
5959
"xterm-for-react": "1.0.4",
60-
"yup": "0.32.11"
60+
"yup": "0.32.11",
61+
"@emoji-mart/data": "^1.0.5",
62+
"@emoji-mart/react": "^1.0.1",
63+
"emoji-mart": "^5.2.1"
6164
},
6265
"devDependencies": {
6366
"@playwright/test": "1.24.1",

site/src/components/ErrorSummary/ErrorSummary.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ export const ErrorSummary: FC<ErrorSummaryProps> = ({
7575
</Collapse>
7676
{retry && (
7777
<div className={styles.retry}>
78-
<Button size="small" onClick={retry} startIcon={<RefreshIcon />} variant="outlined">
78+
<Button
79+
size="small"
80+
onClick={retry}
81+
startIcon={<RefreshIcon />}
82+
variant="outlined"
83+
className={styles.retryButton}
84+
>
7985
{Language.retryMessage}
8086
</Button>
8187
</div>
@@ -122,4 +128,12 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
122128
retry: {
123129
marginTop: `${theme.spacing(2)}px`,
124130
},
131+
retryButton: {
132+
color: theme.palette.error.contrastText,
133+
borderColor: theme.palette.error.contrastText,
134+
135+
"&:hover": {
136+
backgroundColor: theme.palette.error.dark,
137+
},
138+
},
125139
}))

site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import data from "@emoji-mart/data/sets/14/twitter.json"
2+
import Picker from "@emoji-mart/react"
3+
import Button from "@material-ui/core/Button"
14
import InputAdornment from "@material-ui/core/InputAdornment"
5+
import Popover from "@material-ui/core/Popover"
26
import { makeStyles } from "@material-ui/core/styles"
37
import TextField from "@material-ui/core/TextField"
48
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
9+
import { OpenDropdown } from "components/DropdownArrows/DropdownArrows"
510
import { FormFooter } from "components/FormFooter/FormFooter"
611
import { Stack } from "components/Stack/Stack"
712
import { FormikContextType, FormikTouched, useFormik } from "formik"
8-
import { FC } from "react"
13+
import { FC, useRef, useState } from "react"
14+
import { colors } from "theme/colors"
915
import { getFormHelpersWithError, nameValidator, onChangeTrimmed } from "util/formUtils"
1016
import * as Yup from "yup"
1117

@@ -17,6 +23,7 @@ export const Language = {
1723
// This is the same from the CLI on https://github.com/coder/coder/blob/546157b63ef9204658acf58cb653aa9936b70c49/cli/templateedit.go#L59
1824
maxTtlHelperText: "Edit the template maximum time before shutdown in milliseconds",
1925
formAriaLabel: "Template settings form",
26+
selectEmoji: "Select emoji",
2027
}
2128

2229
export const validationSchema = Yup.object({
@@ -43,6 +50,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
4350
isSubmitting,
4451
initialTouched,
4552
}) => {
53+
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false)
4654
const form: FormikContextType<UpdateTemplateMeta> = useFormik<UpdateTemplateMeta>({
4755
initialValues: {
4856
name: template.name,
@@ -59,6 +67,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
5967
const getFieldHelpers = getFormHelpersWithError<UpdateTemplateMeta>(form, error)
6068
const styles = useStyles()
6169
const hasIcon = form.values.icon && form.values.icon !== ""
70+
const emojiButtonRef = useRef<HTMLButtonElement>(null)
6271

6372
return (
6473
<form onSubmit={form.handleSubmit} aria-label={Language.formAriaLabel}>
@@ -83,28 +92,61 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
8392
rows={2}
8493
/>
8594

86-
<TextField
87-
{...getFieldHelpers("icon")}
88-
disabled={isSubmitting}
89-
fullWidth
90-
label={Language.iconLabel}
91-
variant="outlined"
92-
InputProps={{
93-
endAdornment: hasIcon ? (
94-
<InputAdornment position="end">
95-
<img
96-
alt=""
97-
src={form.values.icon}
98-
className={styles.adornment}
99-
// This prevent browser to display the ugly error icon if the
100-
// image path is wrong or user didn't finish typing the url
101-
onError={(e) => (e.currentTarget.style.display = "none")}
102-
onLoad={(e) => (e.currentTarget.style.display = "inline")}
103-
/>
104-
</InputAdornment>
105-
) : undefined,
106-
}}
107-
/>
95+
<div className={styles.iconField}>
96+
<TextField
97+
{...getFieldHelpers("icon")}
98+
disabled={isSubmitting}
99+
fullWidth
100+
label={Language.iconLabel}
101+
variant="outlined"
102+
InputProps={{
103+
endAdornment: hasIcon ? (
104+
<InputAdornment position="end">
105+
<img
106+
alt=""
107+
src={form.values.icon}
108+
className={styles.adornment}
109+
// This prevent browser to display the ugly error icon if the
110+
// image path is wrong or user didn't finish typing the url
111+
onError={(e) => (e.currentTarget.style.display = "none")}
112+
onLoad={(e) => (e.currentTarget.style.display = "inline")}
113+
/>
114+
</InputAdornment>
115+
) : undefined,
116+
}}
117+
/>
118+
119+
<Button
120+
fullWidth
121+
ref={emojiButtonRef}
122+
variant="outlined"
123+
size="small"
124+
endIcon={<OpenDropdown />}
125+
onClick={() => {
126+
setIsEmojiPickerOpen((v) => !v)
127+
}}
128+
>
129+
{Language.selectEmoji}
130+
</Button>
131+
132+
<Popover
133+
id="emoji"
134+
open={isEmojiPickerOpen}
135+
anchorEl={emojiButtonRef.current}
136+
onClose={() => {
137+
setIsEmojiPickerOpen(false)
138+
}}
139+
>
140+
<Picker
141+
theme="dark"
142+
data={data}
143+
onEmojiSelect={(emojiData) => {
144+
form.setFieldValue("icon", `/emojis/${emojiData.unified}.png`)
145+
setIsEmojiPickerOpen(false)
146+
}}
147+
/>
148+
</Popover>
149+
</div>
108150

109151
<TextField
110152
{...getFieldHelpers("max_ttl_ms")}
@@ -123,8 +165,18 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
123165
}
124166

125167
const useStyles = makeStyles((theme) => ({
168+
"@global": {
169+
"em-emoji-picker": {
170+
"--rgb-background": theme.palette.background.paper,
171+
"--rgb-input": colors.gray[17],
172+
"--rgb-color": colors.gray[4],
173+
},
174+
},
126175
adornment: {
127176
width: theme.spacing(3),
128177
height: theme.spacing(3),
129178
},
179+
iconField: {
180+
paddingBottom: theme.spacing(0.5),
181+
},
130182
}))

site/src/theme/overrides.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
99
MuiCssBaseline: {
1010
"@global": {
1111
body: {
12-
backgroundImage: `linear-gradient(to right bottom, ${colors.gray[15]}, ${colors.gray[17]})`,
12+
backgroundImage: `linear-gradient(to right bottom, ${palette.background.default}, ${colors.gray[17]})`,
1313
backgroundRepeat: "no-repeat",
1414
backgroundAttachment: "fixed",
1515
letterSpacing: "-0.015em",
@@ -57,6 +57,12 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
5757
marginLeft: "0 !important",
5858
marginRight: 12,
5959
},
60+
outlined: {
61+
border: `1px solid ${palette.divider}`,
62+
"&:hover": {
63+
backgroundColor: palette.background.default,
64+
},
65+
},
6066
},
6167
MuiIconButton: {
6268
sizeSmall: {
@@ -82,8 +88,8 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
8288
root: {
8389
borderCollapse: "collapse",
8490
border: "none",
85-
background: colors.gray[15],
86-
boxShadow: `0 0 0 1px ${colors.gray[15]} inset`,
91+
background: palette.background.default,
92+
boxShadow: `0 0 0 1px ${palette.background.default} inset`,
8793
overflow: "hidden",
8894

8995
"& td": {

site/static/emojis/1f004.png

551 Bytes

site/static/emojis/1f0cf.png

923 Bytes

site/static/emojis/1f170.png

557 Bytes

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy