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: select group avatars with the emoji picker#11395

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 6 commits intomainfromuser-group-icon-picker
Jan 3, 2024
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
8 changes: 5 additions & 3 deletionssite/src/@types/emoji-mart.d.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,15 +28,17 @@ declare module "@emoji-mart/react" {
| { unified: undefined; src: string }
| { unified: string; src: undefined };

const EmojiPicker: React.FC<{
export interface EmojiMartProps{
set: "native" | "apple" | "facebook" | "google" | "twitter";
theme: "dark" | "light";
data: unknown;
custom: CustomCategory[];
emojiButtonSize?: number;
emojiSize?: number;
onEmojiSelect: (emoji: EmojiData) => void;
}>;
}

const EmojiMart: React.FC<EmojiMartProps>;

export defaultEmojiPicker;
export defaultEmojiMart;
}
15 changes: 10 additions & 5 deletionssite/src/components/ErrorBoundary/ErrorBoundary.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
import { Component,ReactNode, PropsWithChildren } from "react";
import { Component,type ReactNode } from "react";
import { RuntimeErrorState } from "./RuntimeErrorState";

type ErrorBoundaryProps = PropsWithChildren<unknown>;
interface ErrorBoundaryProps {
fallback?: ReactNode;
children: ReactNode;
}

interface ErrorBoundaryState {
error: Error | null;
}

/**
* Our app's Error Boundary
* Read more about React Error Boundaries: https://reactjs.org/docs/error-boundaries.html
* Read more about React Error Boundaries: https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
*/
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
Expand All@@ -20,13 +23,15 @@ export class ErrorBoundary extends Component<
this.state = { error: null };
}

static getDerivedStateFromError(error: Error):{ error: Error } {
static getDerivedStateFromError(error: Error):ErrorBoundaryState {
return { error };
}

render(): ReactNode {
if (this.state.error) {
return <RuntimeErrorState error={this.state.error} />;
return (
this.props.fallback ?? <RuntimeErrorState error={this.state.error} />
);
}

return this.props.children;
Expand Down
40 changes: 40 additions & 0 deletionssite/src/components/IconField/EmojiPicker.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
import EmojiMart, { type EmojiMartProps } from "@emoji-mart/react";
import data from "@emoji-mart/data/sets/14/twitter.json";
import { type FC } from "react";
import icons from "theme/icons.json";

const custom = [
{
id: "icons",
name: "Icons",
emojis: icons.map((icon) => {
const id = icon.split(".")[0];

return {
id,
name: id,
keywords: id.split("-"),
skins: [{ src: `/icon/${icon}` }],
};
}),
},
];

type EmojiPickerProps = Omit<
EmojiMartProps,
"custom" | "data" | "set" | "theme"
>;

const EmojiPicker: FC<EmojiPickerProps> = (props) => {
return (
<EmojiMart
theme="dark"
set="twitter"
data={data}
custom={custom}
{...props}
/>
);
};

export default EmojiPicker;
2 changes: 1 addition & 1 deletionsite/src/components/IconField/IconField.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { action } from "@storybook/addon-actions";
import IconField from "./IconField";
import type { Meta, StoryObj } from "@storybook/react";
import { IconField } from "./IconField";

const meta: Meta<typeof IconField> = {
title: "components/IconField",
Expand Down
94 changes: 47 additions & 47 deletionssite/src/components/IconField/IconField.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,12 +2,11 @@ import { css, Global, useTheme } from "@emotion/react";
import Button from "@mui/material/Button";
import InputAdornment from "@mui/material/InputAdornment";
import TextField, { type TextFieldProps } from "@mui/material/TextField";
import Picker from "@emoji-mart/react";
import { type FC } from "react";
import { visuallyHidden } from "@mui/utils";
import { type FC, lazy, Suspense } from "react";
import { Loader } from "components/Loader/Loader";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import { Stack } from "components/Stack/Stack";
import data from "@emoji-mart/data/sets/14/twitter.json";
import icons from "theme/icons.json";
import {
Popover,
PopoverContent,
Expand All@@ -22,24 +21,12 @@ type IconFieldProps = TextFieldProps & {
onPickEmoji: (value: string) => void;
};

const custom = [
{
id: "icons",
name: "Icons",
emojis: icons.map((icon) => {
const id = icon.split(".")[0];
const EmojiPicker = lazy(() => import("./EmojiPicker"));

return {
id,
name: id,
keywords: id.split("-"),
skins: [{ src: `/icon/${icon}` }],
};
}),
},
];

const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
export const IconField: FC<IconFieldProps> = ({
onPickEmoji,
...textFieldProps
}) => {
if (
typeof textFieldProps.value !== "string" &&
typeof textFieldProps.value !== "undefined"
Expand All@@ -53,9 +40,9 @@ const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
return (
<Stack spacing={1}>
<TextField
{...textFieldProps}
fullWidth
label="Icon"
{...textFieldProps}
InputProps={{
endAdornment: hasIcon ? (
<InputAdornment
Expand DownExpand Up@@ -86,6 +73,18 @@ const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
}}
/>

<Global
styles={css`
em-emoji-picker {
--rgb-background: ${theme.palette.background.paper};
--rgb-input: ${theme.palette.primary.main};
--rgb-color: ${theme.palette.text.primary};

// Hack to prevent the right side from being cut off
width: 350px;
}
`}
/>
<Popover>
{(popover) => (
<>
Expand All@@ -98,35 +97,36 @@ const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
id="emoji"
css={{ marginTop: 0, ".MuiPaper-root": { width: "auto" } }}
>
<Global
styles={css`
em-emoji-picker {
--rgb-background: ${theme.palette.background.paper};
--rgb-input: ${theme.palette.primary.main};
--rgb-color: ${theme.palette.text.primary};

// Hack to prevent the right side from being cut off
width: 350px;
}
`}
/>
<Picker
set="twitter"
theme="dark"
data={data}
custom={custom}
onEmojiSelect={(emoji) => {
const value = emoji.src ?? urlFromUnifiedCode(emoji.unified);
onPickEmoji(value);
popover.setIsOpen(false);
}}
/>
<Suspense fallback={<Loader />}>
<EmojiPicker
onEmojiSelect={(emoji) => {
const value =
emoji.src ?? urlFromUnifiedCode(emoji.unified);
onPickEmoji(value);
popover.setIsOpen(false);
}}
/>
</Suspense>
</PopoverContent>
</>
)}
</Popover>

{/*
- This component takes a long time to load (easily several seconds), so we
don't want to wait until the user actually clicks the button to start loading.
Unfortunately, React doesn't provide an API to start warming a lazy component,
so we just have to sneak it into the DOM, which is kind of annoying, but means
that users shouldn't ever spend time waiting for it to load.
- Except we don't do it when running tests, because Jest doesn't define
`IntersectionObserver`, and it would make them slower anyway. */}
{process.env.NODE_ENV !== "test" && (
<div css={{ ...visuallyHidden }}>
<Suspense>
<EmojiPicker onEmojiSelect={() => {}} />
</Suspense>
</div>
)}
</Stack>
);
};

export default IconField;
11 changes: 0 additions & 11 deletionssite/src/components/IconField/LazyIconField.tsx
View file
Open in desktop

This file was deleted.

5 changes: 2 additions & 3 deletionssite/src/pages/CreateTemplatePage/CreateTemplateForm.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,7 @@ import {
HelpTooltipText,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import {LazyIconField } from "components/IconField/LazyIconField";
import {IconField } from "components/IconField/IconField";
import Link from "@mui/material/Link";
import {
HorizontalForm,
Expand DownExpand Up@@ -345,12 +345,11 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
label="Description"
/>

<LazyIconField
<IconField
{...getFieldHelpers("icon")}
disabled={isSubmitting}
onChange={onChangeTrimmed(form)}
fullWidth
label="Icon"
onPickEmoji={(value) => form.setFieldValue("icon", value)}
/>
</FormFields>
Expand Down
5 changes: 3 additions & 2 deletionssite/src/pages/GroupsPage/CreateGroupPageView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ import TextField from "@mui/material/TextField";
import { CreateGroupRequest } from "api/typesGenerated";
import { FormFooter } from "components/FormFooter/FormFooter";
import { FullPageForm } from "components/FullPageForm/FullPageForm";
import { IconField } from "components/IconField/IconField";
import { Margins } from "components/Margins/Margins";
import { Stack } from "components/Stack/Stack";
import { useFormik } from "formik";
Expand DownExpand Up@@ -58,12 +59,12 @@ export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
fullWidth
label="Display Name"
/>
<TextField
<IconField
{...getFieldHelpers("avatar_url")}
onChange={onChangeTrimmed(form)}
autoComplete="avatar url"
fullWidth
label="Avatar URL"
onPickEmoji={(value) => form.setFieldValue("avatar_url", value)}
/>
</Stack>
<FormFooter onCancel={onCancel} isLoading={isLoading} />
Expand Down
4 changes: 2 additions & 2 deletionssite/src/pages/GroupsPage/SettingsGroupPageView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ import { Group } from "api/typesGenerated";
import { FormFooter } from "components/FormFooter/FormFooter";
import { FullPageForm } from "components/FullPageForm/FullPageForm";
import { Loader } from "components/Loader/Loader";
import {LazyIconField } from "components/IconField/LazyIconField";
import {IconField } from "components/IconField/IconField";
import { Margins } from "components/Margins/Margins";
import { useFormik } from "formik";
import { FC } from "react";
Expand DownExpand Up@@ -84,7 +84,7 @@ const UpdateGroupForm: FC<UpdateGroupFormProps> = ({
label="Display Name"
disabled={isEveryoneGroup(group)}
/>
<LazyIconField
<IconField
{...getFieldHelpers("avatar_url")}
onChange={onChangeTrimmed(form)}
fullWidth
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,7 @@ import {
iconValidator,
} from "utils/formUtils";
import * as Yup from "yup";
import {LazyIconField } from "components/IconField/LazyIconField";
import {IconField } from "components/IconField/IconField";
import {
FormFields,
FormSection,
Expand DownExpand Up@@ -126,7 +126,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
rows={2}
/>

<LazyIconField
<IconField
{...getFieldHelpers("icon")}
disabled={isSubmitting}
onChange={onChangeTrimmed(form)}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp