reorganize code into smaller files & dirs
This commit is contained in:
73
packages/app/src/Components/Textarea/Textarea.css
Normal file
73
packages/app/src/Components/Textarea/Textarea.css
Normal file
@ -0,0 +1,73 @@
|
||||
.rta__autocomplete {
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.rta__list {
|
||||
border: none;
|
||||
}
|
||||
.rta__item:not(:last-child) {
|
||||
border: none;
|
||||
}
|
||||
.rta__entity--selected .user-item,
|
||||
.rta__entity--selected .emoji-item {
|
||||
text-decoration: none;
|
||||
background: var(--gray-secondary);
|
||||
}
|
||||
|
||||
.user-item,
|
||||
.emoji-item {
|
||||
color: var(--font-color);
|
||||
background: var(--gray-superdark);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.user-item:hover,
|
||||
.emoji-item:hover {
|
||||
background: var(--gray-tertiary);
|
||||
}
|
||||
|
||||
.user-item .picture {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.user-picture {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.user-picture .avatar {
|
||||
border-width: 1px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.user-item .nip05 {
|
||||
font-size: var(--font-size-tiny);
|
||||
}
|
||||
|
||||
.emoji-item {
|
||||
font-size: var(--font-size-tiny);
|
||||
}
|
||||
|
||||
.emoji-item .emoji {
|
||||
margin-right: 0.2em;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.emoji-item .emoji-name {
|
||||
font-weight: bold;
|
||||
}
|
95
packages/app/src/Components/Textarea/Textarea.tsx
Normal file
95
packages/app/src/Components/Textarea/Textarea.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import "@webscopeio/react-textarea-autocomplete/style.css";
|
||||
import "./Textarea.css";
|
||||
|
||||
import { useIntl } from "react-intl";
|
||||
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import { NostrPrefix, MetadataCache } from "@snort/system";
|
||||
|
||||
import Avatar from "@/Components/User/Avatar";
|
||||
import Nip05 from "@/Components/User/Nip05";
|
||||
import { hexToBech32 } from "@/Utils";
|
||||
import { UserCache } from "@/Cache";
|
||||
import searchEmoji from "@/Utils/emoji-search";
|
||||
|
||||
import messages from "../messages";
|
||||
|
||||
interface EmojiItemProps {
|
||||
name: string;
|
||||
char: string;
|
||||
}
|
||||
|
||||
const EmojiItem = ({ entity: { name, char } }: { entity: EmojiItemProps }) => {
|
||||
return (
|
||||
<div className="emoji-item">
|
||||
<div className="emoji">{char}</div>
|
||||
<div className="emoji-name">{name}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UserItem = (metadata: MetadataCache) => {
|
||||
const { pubkey, display_name, nip05, ...rest } = metadata;
|
||||
return (
|
||||
<div key={pubkey} className="user-item">
|
||||
<div className="user-picture">
|
||||
<Avatar pubkey={pubkey} user={metadata} />
|
||||
</div>
|
||||
<div className="user-details">
|
||||
<strong>{display_name || rest.name}</strong>
|
||||
<Nip05 nip05={nip05} pubkey={pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface TextareaProps {
|
||||
autoFocus: boolean;
|
||||
className: string;
|
||||
placeholder?: string;
|
||||
onChange(ev: React.ChangeEvent<HTMLTextAreaElement>): void;
|
||||
value: string;
|
||||
onFocus(): void;
|
||||
onKeyDown(ev: React.KeyboardEvent<HTMLTextAreaElement>): void;
|
||||
onDragOver?(ev: React.DragEvent<HTMLTextAreaElement>): void;
|
||||
onDragLeave?(ev: React.DragEvent<HTMLTextAreaElement>): void;
|
||||
onDrop?(ev: React.DragEvent<HTMLTextAreaElement>): void;
|
||||
}
|
||||
|
||||
const Textarea = (props: TextareaProps) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const userDataProvider = async (token: string) => {
|
||||
return await UserCache.search(token);
|
||||
};
|
||||
|
||||
const emojiDataProvider = async (token: string) => {
|
||||
return (await searchEmoji(token)).slice(0, 5).map(({ name, char }) => ({ name, char }));
|
||||
};
|
||||
|
||||
return (
|
||||
// @ts-expect-error If anybody can figure out how to type this, please do
|
||||
<ReactTextareaAutocomplete
|
||||
dir="auto"
|
||||
{...props}
|
||||
loadingComponent={() => <span>Loading...</span>}
|
||||
placeholder={props.placeholder ?? formatMessage(messages.NotePlaceholder)}
|
||||
textAreaComponent={TextareaAutosize}
|
||||
trigger={{
|
||||
":": {
|
||||
dataProvider: emojiDataProvider,
|
||||
component: EmojiItem,
|
||||
output: (item: EmojiItemProps) => item.char,
|
||||
},
|
||||
"@": {
|
||||
afterWhitespace: true,
|
||||
dataProvider: userDataProvider,
|
||||
component: (props: { entity: MetadataCache }) => <UserItem {...props.entity} />,
|
||||
output: (item: { pubkey: string }) => `@${hexToBech32(NostrPrefix.PublicKey, item.pubkey)}`,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Textarea;
|
Reference in New Issue
Block a user