forked from github/plane
feat: added floating toolbar on text selection (#378)
style: re-designed create-issue modal
This commit is contained in:
parent
d28fe930a6
commit
82f8b6d387
@ -21,6 +21,7 @@ import { CreateUpdateCycleModal } from "components/cycles";
|
|||||||
import { CreateLabelModal } from "components/labels";
|
import { CreateLabelModal } from "components/labels";
|
||||||
// ui
|
// ui
|
||||||
import { Button, CustomMenu, Input, Loader } from "components/ui";
|
import { Button, CustomMenu, Input, Loader } from "components/ui";
|
||||||
|
import { PrimaryButton } from "components/ui/button/primary-button";
|
||||||
// icons
|
// icons
|
||||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
// helpers
|
// helpers
|
||||||
@ -156,7 +157,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900">
|
<h3 className="text-xl font-semibold leading-6 text-gray-900">
|
||||||
{status ? "Update" : "Create"} Issue
|
{status ? "Update" : "Create"} Issue
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@ -189,11 +190,11 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
id="name"
|
||||||
label="Title"
|
|
||||||
name="name"
|
name="name"
|
||||||
onChange={handleTitleChange}
|
onChange={handleTitleChange}
|
||||||
className="resize-none"
|
className="resize-none text-xl"
|
||||||
placeholder="Enter title"
|
placeholder="Title"
|
||||||
|
mode="transparent"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
error={errors.name}
|
error={errors.name}
|
||||||
register={register}
|
register={register}
|
||||||
@ -219,7 +220,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
?
|
?
|
||||||
</a>
|
</a>
|
||||||
</Link>{" "}
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -234,9 +235,6 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor={"description"} className="mb-2 text-gray-500">
|
|
||||||
Description
|
|
||||||
</label>
|
|
||||||
<Controller
|
<Controller
|
||||||
name="description"
|
name="description"
|
||||||
control={control}
|
control={control}
|
||||||
@ -245,7 +243,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onJSONChange={(jsonValue) => setValue("description", jsonValue)}
|
onJSONChange={(jsonValue) => setValue("description", jsonValue)}
|
||||||
onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)}
|
onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)}
|
||||||
placeholder="Enter Your Text..."
|
placeholder="Description"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -333,7 +331,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 flex items-center justify-between gap-2">
|
<div className="-mx-5 mt-5 flex items-center justify-between gap-2 border-t px-5 pt-5">
|
||||||
<div
|
<div
|
||||||
className="flex cursor-pointer items-center gap-1"
|
className="flex cursor-pointer items-center gap-1"
|
||||||
onClick={() => setCreateMore((prevData) => !prevData)}
|
onClick={() => setCreateMore((prevData) => !prevData)}
|
||||||
@ -360,15 +358,15 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
<Button type="button" theme="secondary" onClick={handleClose}>
|
<Button type="button" theme="secondary" onClick={handleClose}>
|
||||||
Discard
|
Discard
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={isSubmitting}>
|
<PrimaryButton type="submit" size="sm" loading={isSubmitting}>
|
||||||
{status
|
{status
|
||||||
? isSubmitting
|
? isSubmitting
|
||||||
? "Updating Issue..."
|
? "Updating Issue..."
|
||||||
: "Update Issue"
|
: "Update Issue"
|
||||||
: isSubmitting
|
: isSubmitting
|
||||||
? "Creating Issue..."
|
? "Adding Issue..."
|
||||||
: "Create Issue"}
|
: "Add Issue"}
|
||||||
</Button>
|
</PrimaryButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -231,7 +231,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
<div className="mt-10 flex min-h-full items-start justify-center p-4 text-center sm:p-0 md:mt-20">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
|
@ -185,7 +185,9 @@ export const SingleProjectCard: React.FC<ProjectCardProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-3.5 mb-7">{truncateText(project.description ?? "", 100)}</p>
|
<p className="mt-3.5 mb-7 break-all">
|
||||||
|
{truncateText(project.description ?? "", 100)}
|
||||||
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex h-full items-end justify-between">
|
<div className="flex h-full items-end justify-between">
|
||||||
|
@ -34,9 +34,8 @@ import fileService from "services/file.service";
|
|||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// components
|
// components
|
||||||
import { RichTextToolbar } from "./toolbar";
|
import { CustomFloatingToolbar } from "./toolbar/float-tool-tip";
|
||||||
import { MentionAutoComplete } from "./mention-autocomplete";
|
import { MentionAutoComplete } from "./mention-autocomplete";
|
||||||
import { FloatingLinkToolbar } from "./toolbar/link";
|
|
||||||
|
|
||||||
export interface IRemirrorRichTextEditor {
|
export interface IRemirrorRichTextEditor {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@ -51,6 +50,9 @@ export interface IRemirrorRichTextEditor {
|
|||||||
customClassName?: string;
|
customClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-duplicate-imports
|
||||||
|
import { FloatingWrapper, FloatingToolbar } from "@remirror/react";
|
||||||
|
|
||||||
const RemirrorRichTextEditor: FC<IRemirrorRichTextEditor> = (props) => {
|
const RemirrorRichTextEditor: FC<IRemirrorRichTextEditor> = (props) => {
|
||||||
const {
|
const {
|
||||||
placeholder,
|
placeholder,
|
||||||
@ -182,30 +184,42 @@ const RemirrorRichTextEditor: FC<IRemirrorRichTextEditor> = (props) => {
|
|||||||
<Remirror
|
<Remirror
|
||||||
manager={manager}
|
manager={manager}
|
||||||
initialContent={state}
|
initialContent={state}
|
||||||
classNames={[`p-4 focus:outline-none ${customClassName}`]}
|
classNames={[
|
||||||
|
`p-4 relative focus:outline-none rounded-md border border-transparent focus:border-theme ${customClassName}`,
|
||||||
|
]}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
onBlur(jsonValue, htmlValue);
|
onBlur(jsonValue, htmlValue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="rounded-md border">
|
{(!value || value === "" || value?.content?.[0]?.content === undefined) && (
|
||||||
{showToolbar && editable && (
|
<p className="pointer-events-none absolute top-[8.8rem] left-9 text-gray-300">
|
||||||
<div className="box-border w-full border-b py-2">
|
{placeholder || "Enter text..."}
|
||||||
<RichTextToolbar />
|
</p>
|
||||||
</div>
|
)}
|
||||||
)}
|
<EditorComponent />
|
||||||
<EditorComponent />
|
|
||||||
{imageLoader && (
|
{imageLoader && (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* <TableComponents /> */}
|
|
||||||
<FloatingLinkToolbar />
|
{editable && (
|
||||||
<MentionAutoComplete mentions={mentions} tags={tags} />
|
<FloatingWrapper
|
||||||
{<OnChangeJSON onChange={handleJSONChange} />}
|
positioner="always"
|
||||||
{<OnChangeHTML onChange={handleHTMLChange} />}
|
floatingLabel="Custom Floating Toolbar"
|
||||||
</div>
|
renderOutsideEditor
|
||||||
|
>
|
||||||
|
<FloatingToolbar className="z-[9999] overflow-hidden rounded">
|
||||||
|
<CustomFloatingToolbar />
|
||||||
|
</FloatingToolbar>
|
||||||
|
</FloatingWrapper>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<MentionAutoComplete mentions={mentions} tags={tags} />
|
||||||
|
{<OnChangeJSON onChange={handleJSONChange} />}
|
||||||
|
{<OnChangeHTML onChange={handleHTMLChange} />}
|
||||||
</Remirror>
|
</Remirror>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
// buttons
|
||||||
|
import {
|
||||||
|
ToggleBoldButton,
|
||||||
|
ToggleItalicButton,
|
||||||
|
ToggleUnderlineButton,
|
||||||
|
ToggleStrikeButton,
|
||||||
|
ToggleOrderedListButton,
|
||||||
|
ToggleBulletListButton,
|
||||||
|
ToggleCodeButton,
|
||||||
|
} from "@remirror/react";
|
||||||
|
|
||||||
|
import HeadingControls from "./heading-controls";
|
||||||
|
|
||||||
|
export const CustomFloatingToolbar: React.FC = () => (
|
||||||
|
<div className="z-[99999] flex items-center gap-y-2 divide-x rounded border bg-white p-1 px-0.5 shadow-md">
|
||||||
|
<div className="flex items-center gap-x-1 px-2">
|
||||||
|
<HeadingControls />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 px-2">
|
||||||
|
<ToggleBoldButton />
|
||||||
|
<ToggleItalicButton />
|
||||||
|
<ToggleUnderlineButton />
|
||||||
|
<ToggleStrikeButton />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 px-2">
|
||||||
|
<ToggleOrderedListButton />
|
||||||
|
<ToggleBulletListButton />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-x-1 px-2">
|
||||||
|
<ToggleCodeButton />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
@ -173,12 +173,12 @@ export const FloatingLinkToolbar = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
<FloatingToolbar className="shadow-lg rounded bg-white p-1">
|
<FloatingToolbar className="rounded bg-white p-1 shadow-lg">
|
||||||
{linkEditButtons}
|
{linkEditButtons}
|
||||||
</FloatingToolbar>
|
</FloatingToolbar>
|
||||||
)}
|
)}
|
||||||
{!isEditing && empty && (
|
{!isEditing && empty && (
|
||||||
<FloatingToolbar positioner={linkPositioner} className="shadow-lg rounded bg-white p-1">
|
<FloatingToolbar positioner={linkPositioner} className="rounded bg-white p-1 shadow-lg">
|
||||||
{linkEditButtons}
|
{linkEditButtons}
|
||||||
</FloatingToolbar>
|
</FloatingToolbar>
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user