feat: Add emoji picker for template icons (#3601)

This commit is contained in:
Bruno Quaresma 2022-08-19 17:42:05 -03:00 committed by GitHub
parent a4c90c591d
commit 54b8e794ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3697 changed files with 128 additions and 34 deletions

View File

@ -9,6 +9,7 @@ MacOS = "macOS"
[files]
extend-exclude = [
"**.svg",
"**.png",
"**.lock",
"go.sum",
"go.mod",

View File

@ -1,6 +0,0 @@
declare module "can-ndjson-stream" {
function ndjsonStream<TValueType>(
body: ReadableStream<Uint8Array> | null,
): Promise<ReadableStream<TValueType>>
export default ndjsonStream
}

9
site/emoji-mart.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare module "@emoji-mart/react" {
const Picker: React.FC<{
theme: "dark" | "light"
data: Record<string, unknown>
onEmojiSelect: (emojiData: { unified: string }) => void
}>
export default Picker
}

View File

@ -57,7 +57,10 @@
"xterm-addon-web-links": "0.6.0",
"xterm-addon-webgl": "0.11.4",
"xterm-for-react": "1.0.4",
"yup": "0.32.11"
"yup": "0.32.11",
"@emoji-mart/data": "^1.0.5",
"@emoji-mart/react": "^1.0.1",
"emoji-mart": "^5.2.1"
},
"devDependencies": {
"@playwright/test": "1.24.1",

View File

@ -75,7 +75,13 @@ export const ErrorSummary: FC<ErrorSummaryProps> = ({
</Collapse>
{retry && (
<div className={styles.retry}>
<Button size="small" onClick={retry} startIcon={<RefreshIcon />} variant="outlined">
<Button
size="small"
onClick={retry}
startIcon={<RefreshIcon />}
variant="outlined"
className={styles.retryButton}
>
{Language.retryMessage}
</Button>
</div>
@ -122,4 +128,12 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
retry: {
marginTop: `${theme.spacing(2)}px`,
},
retryButton: {
color: theme.palette.error.contrastText,
borderColor: theme.palette.error.contrastText,
"&:hover": {
backgroundColor: theme.palette.error.dark,
},
},
}))

View File

@ -1,11 +1,17 @@
import data from "@emoji-mart/data/sets/14/twitter.json"
import Picker from "@emoji-mart/react"
import Button from "@material-ui/core/Button"
import InputAdornment from "@material-ui/core/InputAdornment"
import Popover from "@material-ui/core/Popover"
import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
import { OpenDropdown } from "components/DropdownArrows/DropdownArrows"
import { FormFooter } from "components/FormFooter/FormFooter"
import { Stack } from "components/Stack/Stack"
import { FormikContextType, FormikTouched, useFormik } from "formik"
import { FC } from "react"
import { FC, useRef, useState } from "react"
import { colors } from "theme/colors"
import { getFormHelpersWithError, nameValidator, onChangeTrimmed } from "util/formUtils"
import * as Yup from "yup"
@ -17,6 +23,7 @@ export const Language = {
// This is the same from the CLI on https://github.com/coder/coder/blob/546157b63ef9204658acf58cb653aa9936b70c49/cli/templateedit.go#L59
maxTtlHelperText: "Edit the template maximum time before shutdown in milliseconds",
formAriaLabel: "Template settings form",
selectEmoji: "Select emoji",
}
export const validationSchema = Yup.object({
@ -43,6 +50,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
isSubmitting,
initialTouched,
}) => {
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false)
const form: FormikContextType<UpdateTemplateMeta> = useFormik<UpdateTemplateMeta>({
initialValues: {
name: template.name,
@ -59,6 +67,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
const getFieldHelpers = getFormHelpersWithError<UpdateTemplateMeta>(form, error)
const styles = useStyles()
const hasIcon = form.values.icon && form.values.icon !== ""
const emojiButtonRef = useRef<HTMLButtonElement>(null)
return (
<form onSubmit={form.handleSubmit} aria-label={Language.formAriaLabel}>
@ -83,28 +92,61 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
rows={2}
/>
<TextField
{...getFieldHelpers("icon")}
disabled={isSubmitting}
fullWidth
label={Language.iconLabel}
variant="outlined"
InputProps={{
endAdornment: hasIcon ? (
<InputAdornment position="end">
<img
alt=""
src={form.values.icon}
className={styles.adornment}
// This prevent browser to display the ugly error icon if the
// image path is wrong or user didn't finish typing the url
onError={(e) => (e.currentTarget.style.display = "none")}
onLoad={(e) => (e.currentTarget.style.display = "inline")}
/>
</InputAdornment>
) : undefined,
}}
/>
<div className={styles.iconField}>
<TextField
{...getFieldHelpers("icon")}
disabled={isSubmitting}
fullWidth
label={Language.iconLabel}
variant="outlined"
InputProps={{
endAdornment: hasIcon ? (
<InputAdornment position="end">
<img
alt=""
src={form.values.icon}
className={styles.adornment}
// This prevent browser to display the ugly error icon if the
// image path is wrong or user didn't finish typing the url
onError={(e) => (e.currentTarget.style.display = "none")}
onLoad={(e) => (e.currentTarget.style.display = "inline")}
/>
</InputAdornment>
) : undefined,
}}
/>
<Button
fullWidth
ref={emojiButtonRef}
variant="outlined"
size="small"
endIcon={<OpenDropdown />}
onClick={() => {
setIsEmojiPickerOpen((v) => !v)
}}
>
{Language.selectEmoji}
</Button>
<Popover
id="emoji"
open={isEmojiPickerOpen}
anchorEl={emojiButtonRef.current}
onClose={() => {
setIsEmojiPickerOpen(false)
}}
>
<Picker
theme="dark"
data={data}
onEmojiSelect={(emojiData) => {
form.setFieldValue("icon", `/emojis/${emojiData.unified}.png`)
setIsEmojiPickerOpen(false)
}}
/>
</Popover>
</div>
<TextField
{...getFieldHelpers("max_ttl_ms")}
@ -123,8 +165,18 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
}
const useStyles = makeStyles((theme) => ({
"@global": {
"em-emoji-picker": {
"--rgb-background": theme.palette.background.paper,
"--rgb-input": colors.gray[17],
"--rgb-color": colors.gray[4],
},
},
adornment: {
width: theme.spacing(3),
height: theme.spacing(3),
},
iconField: {
paddingBottom: theme.spacing(0.5),
},
}))

View File

@ -9,7 +9,7 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
MuiCssBaseline: {
"@global": {
body: {
backgroundImage: `linear-gradient(to right bottom, ${colors.gray[15]}, ${colors.gray[17]})`,
backgroundImage: `linear-gradient(to right bottom, ${palette.background.default}, ${colors.gray[17]})`,
backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
letterSpacing: "-0.015em",
@ -57,6 +57,12 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
marginLeft: "0 !important",
marginRight: 12,
},
outlined: {
border: `1px solid ${palette.divider}`,
"&:hover": {
backgroundColor: palette.background.default,
},
},
},
MuiIconButton: {
sizeSmall: {
@ -82,8 +88,8 @@ export const getOverrides = ({ palette, breakpoints }: Theme): Overrides => {
root: {
borderCollapse: "collapse",
border: "none",
background: colors.gray[15],
boxShadow: `0 0 0 1px ${colors.gray[15]} inset`,
background: palette.background.default,
boxShadow: `0 0 0 1px ${palette.background.default} inset`,
overflow: "hidden",
"& td": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Some files were not shown because too many files have changed in this diff Show More