mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
expandable composer
This commit is contained in:
parent
f4b2458417
commit
d4eb237e40
@ -32,13 +32,13 @@ export function ChatsList() {
|
|||||||
if (status === 'loading') {
|
if (status === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="inline-flex h-9 items-center gap-2.5 rounded-md px-2.5">
|
<div className="inline-flex h-10 items-center gap-2.5 pl-4 border-l-2 border-transparent">
|
||||||
<div className="relative h-6 w-6 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-3 w-full animate-pulse rounded-sm bg-white/10 backdrop-blur-xl" />
|
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
<div className="inline-flex h-9 items-center gap-2.5 rounded-md px-2.5">
|
<div className="inline-flex h-10 items-center gap-2.5 pl-4 border-l-2 border-transparent">
|
||||||
<div className="relative h-6 w-6 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="relative h-7 w-7 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-3 w-full animate-pulse rounded-sm bg-white/10 backdrop-blur-xl" />
|
<div className="h-4 w-full animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,6 @@ import { nip19 } from 'nostr-tools';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
import { Button } from '@shared/button';
|
|
||||||
import { Suggestion } from '@shared/composer';
|
import { Suggestion } from '@shared/composer';
|
||||||
import { CancelIcon, LoaderIcon, PlusCircleIcon } from '@shared/icons';
|
import { CancelIcon, LoaderIcon, PlusCircleIcon } from '@shared/icons';
|
||||||
import { MentionNote } from '@shared/notes';
|
import { MentionNote } from '@shared/notes';
|
||||||
@ -25,6 +24,7 @@ export function Composer() {
|
|||||||
const [status, setStatus] = useState<null | 'loading' | 'done'>(null);
|
const [status, setStatus] = useState<null | 'loading' | 'done'>(null);
|
||||||
const [reply, clearReply] = useComposer((state) => [state.reply, state.clearReply]);
|
const [reply, clearReply] = useComposer((state) => [state.reply, state.clearReply]);
|
||||||
|
|
||||||
|
const expand = useComposer((state) => state.expand)
|
||||||
const upload = useImageUploader();
|
const upload = useImageUploader();
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
@ -51,10 +51,7 @@ export function Composer() {
|
|||||||
content: '',
|
content: '',
|
||||||
editorProps: {
|
editorProps: {
|
||||||
attributes: {
|
attributes: {
|
||||||
class: twMerge(
|
class: 'h-full markdown break-all overflow-y-auto outline-none pr-2',
|
||||||
'scrollbar-hide markdown break-all max-h-[500px] overflow-y-auto outline-none pr-2',
|
|
||||||
`${reply.id ? '!min-h-42' : '!min-h-[120px]'}`
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -127,9 +124,9 @@ export function Composer() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col px-4 pb-4">
|
<div className="flex h-full flex-col">
|
||||||
<div className="flex h-full w-full gap-3">
|
<div className="flex h-full w-full gap-3 px-4 pb-4">
|
||||||
<div className="flex w-8 shrink-0 items-center justify-center">
|
<div className="flex w-10 shrink-0 items-center justify-center">
|
||||||
<div className="h-full w-[2px] bg-white/10 backdrop-blur-xl" />
|
<div className="h-full w-[2px] bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
@ -139,6 +136,7 @@ export function Composer() {
|
|||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
|
className={twMerge('scrollbar-hide markdown break-all max-h-[500px] overflow-y-auto outline-none pr-2', expand ? 'min-h-[500px]' : 'min-h-[120px]')}
|
||||||
/>
|
/>
|
||||||
{reply.id && (
|
{reply.id && (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -154,21 +152,21 @@ export function Composer() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex items-center justify-between">
|
<div className="flex items-center justify-between bg-white/5 rounded-b-xl p-2 border-t border-white/10">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => uploadImage()}
|
onClick={() => uploadImage()}
|
||||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
className="ml-2 inline-flex h-10 w-10 items-center justify-center rounded-lg backdrop-blur-xl hover:bg-white/10"
|
||||||
>
|
>
|
||||||
<PlusCircleIcon className="h-5 w-5 text-white/50" />
|
<PlusCircleIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</button>
|
||||||
<Button onClick={() => submit()} preset="publish">
|
<button onClick={() => submit()} className="inline-flex items-center justify-center w-max px-8 rounded-lg font-bold h-10 bg-fuchsia-500 hover:bg-fuchsia-600">
|
||||||
{status === 'loading' ? (
|
{status === 'loading' ? (
|
||||||
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
||||||
) : (
|
) : (
|
||||||
'Post'
|
'Post'
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
@ -8,16 +9,22 @@ import {
|
|||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
ComposeIcon,
|
ComposeIcon,
|
||||||
|
ExpandIcon,
|
||||||
} from '@shared/icons';
|
} from '@shared/icons';
|
||||||
|
|
||||||
import { useComposer } from '@stores/composer';
|
import { useComposer } from '@stores/composer';
|
||||||
|
|
||||||
export function ComposerModal() {
|
export function ComposerModal() {
|
||||||
const { db } = useStorage();
|
const { db } = useStorage();
|
||||||
const [toggle, open] = useComposer((state) => [state.toggleModal, state.open]);
|
|
||||||
|
const [toggleModal, open] = useComposer((state) => [state.toggleModal, state.open]);
|
||||||
|
const [toggleExpand, expand] = useComposer((state) => [
|
||||||
|
state.toggleExpand,
|
||||||
|
state.expand,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={open} onOpenChange={toggle}>
|
<Dialog.Root open={open} onOpenChange={toggleModal}>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -30,7 +37,12 @@ export function ComposerModal() {
|
|||||||
<Dialog.Portal className="relative z-10">
|
<Dialog.Portal className="relative z-10">
|
||||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
|
||||||
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||||
<div className="relative h-min w-full max-w-2xl rounded-xl bg-white/10 backdrop-blur-xl">
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
'relative h-min w-full rounded-xl bg-white/10 backdrop-blur-xl',
|
||||||
|
expand ? 'max-w-4xl' : 'max-w-2xl'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between px-4 py-4">
|
<div className="flex items-center justify-between px-4 py-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ComposerUser pubkey={db.account.pubkey} />
|
<ComposerUser pubkey={db.account.pubkey} />
|
||||||
@ -42,12 +54,18 @@ export function ComposerModal() {
|
|||||||
<ChevronDownIcon className="h-4 w-4" />
|
<ChevronDownIcon className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Close
|
<div className="inline-flex items-center gap-2">
|
||||||
onClick={() => toggle(false)}
|
<button
|
||||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
type="button"
|
||||||
>
|
onClick={() => toggleExpand()}
|
||||||
<CancelIcon className="h-5 w-5 text-white/50" />
|
className="inline-flex h-10 w-10 items-center justify-center rounded-lg backdrop-blur-xl hover:bg-white/10"
|
||||||
</Dialog.Close>
|
>
|
||||||
|
<ExpandIcon className="h-5 w-5 text-white/50" />
|
||||||
|
</button>
|
||||||
|
<Dialog.Close className="inline-flex h-10 w-10 items-center justify-center rounded-lg backdrop-blur-xl hover:bg-white/10">
|
||||||
|
<CancelIcon className="h-5 w-5 text-white/50" />
|
||||||
|
</Dialog.Close>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Composer />
|
<Composer />
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ export function ComposerUser({ pubkey }: { pubkey: string }) {
|
|||||||
<Image
|
<Image
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className="h-8 w-8 shrink-0 rounded-md object-cover"
|
className="h-10 w-10 shrink-0 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h5 className="text-base font-semibold leading-none text-white">
|
<h5 className="text-base font-semibold leading-none text-white">
|
||||||
{user?.nip05 || user?.name || (
|
{user?.nip05 || user?.name || (
|
||||||
|
22
src/shared/icons/expand.tsx
Normal file
22
src/shared/icons/expand.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export function ExpandIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
d="M13.75 3.75h5.75a.75.75 0 01.75.75v5.75m-16.5 3.5v5.75c0 .414.336.75.75.75h5.75M19.5 4.5L14 10m-4 4l-5.5 5.5"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
@ -51,3 +51,4 @@ export * from './arrowRightCircle';
|
|||||||
export * from './hashtag';
|
export * from './hashtag';
|
||||||
export * from './file';
|
export * from './file';
|
||||||
export * from './share';
|
export * from './share';
|
||||||
|
export * from './expand';
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
interface ComposerState {
|
interface ComposerState {
|
||||||
|
expand: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
reply: { id: string; pubkey: string; root?: string };
|
reply: { id: string; pubkey: string; root?: string };
|
||||||
toggleModal: (status: boolean) => void;
|
toggleModal: () => void;
|
||||||
|
toggleExpand: () => void;
|
||||||
setReply: (id: string, pubkey: string, root?: string) => void;
|
setReply: (id: string, pubkey: string, root?: string) => void;
|
||||||
clearReply: () => void;
|
clearReply: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useComposer = create<ComposerState>((set) => ({
|
export const useComposer = create<ComposerState>((set) => ({
|
||||||
|
expand: false,
|
||||||
open: false,
|
open: false,
|
||||||
reply: { id: null, pubkey: null, root: null },
|
reply: { id: null, pubkey: null, root: null },
|
||||||
toggleModal: (status: boolean) => {
|
toggleModal: () => {
|
||||||
set({ open: status });
|
set((state) => ({ open: !state.open }));
|
||||||
|
},
|
||||||
|
toggleExpand: () => {
|
||||||
|
set((state) => ({ expand: !state.expand }));
|
||||||
},
|
},
|
||||||
setReply: (id: string, pubkey: string, root?: string) => {
|
setReply: (id: string, pubkey: string, root?: string) => {
|
||||||
set({ reply: { id: id, pubkey: pubkey, root: root } });
|
set({ reply: { id: id, pubkey: pubkey, root: root } });
|
||||||
|
Loading…
Reference in New Issue
Block a user