Add mention support

Allow vertical resizing of inputs
This commit is contained in:
jude 2024-03-03 21:44:35 +00:00
parent 66135ecd08
commit 329492b244
11 changed files with 219 additions and 78 deletions

View File

@ -8,10 +8,10 @@
"dependencies": {
"axios": "^1.5.1",
"bulma": "^0.9.4",
"humanize-duration": "^3.31.0",
"luxon": "^3.4.3",
"preact": "^10.13.1",
"react-colorful": "^5.6.1",
"react-mentions": "^4.4.10",
"react-query": "^3.39.3",
"tributejs": "^5.1.3",
"use-debounce": "^10.0.0",
@ -3322,11 +3322,6 @@
"he": "bin/he"
}
},
"node_modules/humanize-duration": {
"version": "3.31.0",
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.31.0.tgz",
"integrity": "sha512-fRrehgBG26NNZysRlTq1S+HPtDpp3u+Jzdc/d5A4cEzOD86YLAkDaJyJg8krSdCi7CJ+s7ht3fwRj8Dl+Btd0w=="
},
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@ -3395,6 +3390,14 @@
"node": ">= 0.4"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-array-buffer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
@ -4075,7 +4078,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -4382,7 +4384,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@ -4484,8 +4485,35 @@
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-mentions": {
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.4.10.tgz",
"integrity": "sha512-JHiQlgF1oSZR7VYPjq32wy97z1w1oE4x10EuhKjPr4WUKhVzG1uFQhQjKqjQkbVqJrmahf+ldgBTv36NrkpKpA==",
"dependencies": {
"@babel/runtime": "7.4.5",
"invariant": "^2.2.4",
"prop-types": "^15.5.8",
"substyle": "^9.1.0"
},
"peerDependencies": {
"react": ">=16.8.3",
"react-dom": ">=16.8.3"
}
},
"node_modules/react-mentions/node_modules/@babel/runtime": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
"dependencies": {
"regenerator-runtime": "^0.13.2"
}
},
"node_modules/react-mentions/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/react-onclickoutside": {
"version": "6.13.0",
@ -4963,6 +4991,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/substyle": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz",
"integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==",
"dependencies": {
"@babel/runtime": "^7.3.4",
"invariant": "^2.2.4"
},
"peerDependencies": {
"react": ">=16.8.3"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@ -5,8 +5,7 @@
"scripts": {
"dev": "vite build --watch --mode development",
"build": "vite build",
"preview": "vite preview",
"package": "mkdir -p reminder-dashboard/usr/share/reminder-dashboard && vite build --mode production --outDir reminder-dashboard/usr/share/reminder-dashboard && dpkg-deb --build reminder-dashboard"
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.5.1",

View File

@ -0,0 +1,6 @@
import { useParams } from "wouter";
export const useGuild = () => {
const { guild } = useParams() as { guild: string };
return guild;
};

View File

@ -0,0 +1,28 @@
import { useEffect, useMemo } from "preact/hooks";
import { useQuery } from "react-query";
import { fetchGuildRoles } from "../../api";
import Tribute from "tributejs";
import { useGuild } from "./useGuild";
export const useMentions = (input) => {
const guild = useGuild();
const { data: roles } = useQuery(fetchGuildRoles(guild));
const tribute = useMemo(() => {
return new Tribute({
values: (roles || []).map(({ id, name }) => ({ key: name, value: id })),
allowSpaces: true,
selectTemplate: (item) => {
return `<@&${item.original.value}>`;
},
});
}, [roles]);
useEffect(() => {
tribute.detach(input.current);
if (input.current !== null) {
tribute.attach(input.current);
}
}, [tribute]);
};

View File

@ -1,8 +1,13 @@
import { useReminder } from "./ReminderContext";
import { useRef } from "preact/hooks";
import { useMentions } from "../App/useMentions";
export const Content = () => {
const [reminder, setReminder] = useReminder();
const input = useRef(null);
useMentions(input);
return (
<>
<label class="is-sr-only">Content</label>
@ -12,6 +17,7 @@ export const Content = () => {
maxlength={2000}
name="content"
rows={1}
ref={input}
value={reminder.content}
onInput={(ev) => {
setReminder((reminder) => ({

View File

@ -1,5 +1,7 @@
import { ImagePicker } from "../ImagePicker";
import { Reminder } from "../../../api";
import { useMentions } from "../../App/useMentions";
import { useRef } from "preact/hooks";
type Props = {
name: string;
@ -8,6 +10,9 @@ type Props = {
};
export const Author = ({ name, icon, setReminder }: Props) => {
const input = useRef(null);
useMentions(input);
return (
<div class="embed-author-box">
<div class="a">
@ -34,6 +39,7 @@ export const Author = ({ name, icon, setReminder }: Props) => {
class="discord-embed-author message-input autoresize"
placeholder="Embed Author..."
rows={1}
ref={input}
maxlength={256}
name="embed_author"
value={name}

View File

@ -1,4 +1,11 @@
export const Description = ({ description, onInput }) => (
import { useMentions } from "../../App/useMentions";
import { useRef } from "preact/hooks";
export const Description = ({ description, onInput }) => {
const input = useRef(null);
useMentions(input);
return (
<>
<label class="is-sr-only" for="embedDescription">
Embed Description
@ -9,6 +16,7 @@ export const Description = ({ description, onInput }) => (
maxlength={4096}
name="embed_description"
rows={1}
ref={input}
value={description}
onInput={(ev) => {
onInput(ev.currentTarget.value);
@ -16,3 +24,4 @@ export const Description = ({ description, onInput }) => (
></textarea>
</>
);
};

View File

@ -1,4 +1,10 @@
import { useRef } from "preact/hooks";
import { useMentions } from "../../../App/useMentions";
export const Field = ({ title, value, inline, index, onUpdate }) => {
const input = useRef(null);
useMentions(input);
return (
<div data-inlined={inline ? "1" : "0"} class="embed-field-box" key={index}>
<label class="is-sr-only" for="embedFieldTitle">
@ -44,6 +50,7 @@ export const Field = ({ title, value, inline, index, onUpdate }) => {
maxlength={1024}
name="embed_field_value[]"
rows={1}
ref={input}
value={value}
onInput={(ev) =>
onUpdate({

View File

@ -1,5 +1,7 @@
import { Reminder } from "../../../api";
import { ImagePicker } from "../ImagePicker";
import { useMentions } from "../../App/useMentions";
import { useRef } from "preact/hooks";
type Props = {
footer: string;
@ -7,7 +9,11 @@ type Props = {
setReminder: (r: (reminder: Reminder) => Reminder) => void;
};
export const Footer = ({ footer, icon, setReminder }: Props) => (
export const Footer = ({ footer, icon, setReminder }: Props) => {
const input = useRef(null);
useMentions(input);
return (
<div class="embed-footer-box">
<p class="image is-20x20 customizable">
<ImagePicker
@ -31,6 +37,7 @@ export const Footer = ({ footer, icon, setReminder }: Props) => (
maxlength={2048}
name="embed_footer"
rows={1}
ref={input}
value={footer}
onInput={(ev) => {
setReminder((reminder) => ({
@ -41,3 +48,4 @@ export const Footer = ({ footer, icon, setReminder }: Props) => (
></textarea>
</div>
);
};

View File

@ -1,4 +1,12 @@
export const Title = ({ title, onInput }) => (
import { useParams } from "wouter";
import { useRef } from "preact/hooks";
import { useMentions } from "../../App/useMentions";
export const Title = ({ title, onInput }) => {
const input = useRef(null);
useMentions(input);
return (
<>
<label class="is-sr-only" for="embedTitle">
Embed Title
@ -8,6 +16,7 @@ export const Title = ({ title, onInput }) => (
placeholder="Embed Title..."
maxlength={256}
rows={1}
ref={input}
name="embed_title"
value={title}
onInput={(ev) => {
@ -16,3 +25,4 @@ export const Title = ({ title, onInput }) => (
></textarea>
</>
);
};

View File

@ -4,3 +4,25 @@
flex-direction: column;
font-style: italic;
}
.tribute-container {
background-color: #2b2d31;
color: #fff;
border-radius: 8px;
margin: 4px;
padding: 4px;
box-shadow: 0 0 5px 0 rgba(0,0,0,0.75);
.highlight {
background-color: #35373c;
}
li {
padding: 8px 12px;
border-radius: 8px;
}
}
textarea.autoresize {
resize: vertical !important;
}