予約投稿機能

This commit is contained in:
Ebise Lutica 2023-05-26 01:48:25 +09:00
parent fcdb85c90f
commit 3d63c31da3
17 changed files with 444 additions and 71 deletions

View File

@ -13,7 +13,7 @@
"storybook": "pnpm recursive --filter tools-frontend run storybook"
},
"devDependencies": {
"turbo": "^1.9.3"
"turbo": "^1.9.9"
},
"packageManager": "pnpm@8.3.1"
"packageManager": "pnpm@8.5.1"
}

View File

@ -3,4 +3,6 @@ import { PrismaClient } from '@prisma/client';
/**
* Prisma ORMクライアント
*/
export const prisma = new PrismaClient();
export const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
});

View File

@ -23,6 +23,7 @@ export const callbackMiauthController: RouteHandler<{Querystring: {session: stri
return;
}
const url = `https://${host}/api/miauth/${session}/check`;
const res = await axios.post(url, {});
const { token, user } = res.data;
@ -34,6 +35,7 @@ export const callbackMiauthController: RouteHandler<{Querystring: {session: stri
return;
}
console.log(`Try to get a Misskey access token from ${host} with session ${session}...`);
const accessToken = await processLogin(user, host, token);
await reply.view('frontend', { token: accessToken });
};

View File

@ -13,6 +13,6 @@ export const generateAccessToken = async (): Promise<string> => {
do {
token = rndstr(32);
used = await prisma.usedToken.findUnique({ where: { token } });
} while (used !== undefined);
} while (used != null);
return token;
};

View File

@ -8,7 +8,7 @@ export const getScheduledNotes = async (account: Account) => {
const notes = await prisma.scheduledNote.findMany({
where: { misskeySessionId: { in: sessionIds } },
orderBy: { date: 'asc' },
orderBy: { date: 'desc' },
select: {
id: true,
misskeySessionId: true,

View File

@ -14,6 +14,7 @@
"dependencies": {
"@babel/preset-react": "^7.18.6",
"@radix-ui/colors": "^0.1.8",
"@radix-ui/react-alert-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-label": "^2.0.1",
"@radix-ui/react-radio-group": "^1.1.2",
@ -72,7 +73,7 @@
"tools-backend": "workspace:*",
"tools-tsconfig": "workspace:*",
"typescript": "5.0.4",
"vite": "^4.3.3",
"vite": "^4.3.8",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.30.1"
}

View File

@ -17,6 +17,7 @@ const AnnouncementsPage = lazy(() => import('@/pages/announcements'));
const AboutPage = lazy(() => import('@/pages/about'));
const AppsNoteScheduler = lazy(() => import('@/pages/apps/note-scheduler'));
const AppsNoteSchedulerNew = lazy(() => import('@/pages/apps/note-scheduler.new'));
const AppsNoteSchedulerEdit = lazy(() => import('@/pages/apps/note-scheduler.edit'));
const NotFound = lazy(() => import('@/pages/not-found'));
export const App : React.FC = () => {
@ -37,6 +38,7 @@ export const App : React.FC = () => {
<Route path="/about" element={<AboutPage />}/>
<Route path="/apps/note-scheduler" element={<AppsNoteScheduler />}/>
<Route path="/apps/note-scheduler/new" element={<AppsNoteSchedulerNew />}/>
<Route path="/apps/note-scheduler/edit/:id" element={<AppsNoteSchedulerEdit />}/>
<Route path="*" element={<NotFound />}/>
</Routes>
<VStack as="footer" alignItems="center" css={{ padding: '$2xl $m' }}>

View File

@ -21,6 +21,7 @@ import { trpc } from '@/libs/trpc';
export type NoteSchedulerFormProp = {
id?: string;
disabled?: boolean;
initialNote?: Draft;
onSubmit?: (draft: Draft) => void;
};
@ -50,6 +51,7 @@ export const NoteSchedulerForm: React.FC<NoteSchedulerFormProp> = (p) => {
}), [sessions]);
const [draft, setDraft] = useState<Draft>(draftDefault);
const [dateError, setDateError] = useState<string | null>(null);
useEffect(() => {
if (!p.id) {
@ -69,6 +71,11 @@ export const NoteSchedulerForm: React.FC<NoteSchedulerFormProp> = (p) => {
});
}, [draftDefault, notes, p.id]);
useEffect(() => {
if (!p.initialNote) return;
setDraft(p.initialNote);
}, [p.initialNote]);
const updateDraft = useCallback(<T extends keyof Draft>(key: T, value: Draft[T]) => {
setDraft({
...draft,
@ -76,6 +83,17 @@ export const NoteSchedulerForm: React.FC<NoteSchedulerFormProp> = (p) => {
});
}, [draft]);
const onChangeDate = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
updateDraft('dateString', e.target.value);
if (Date.now() >= Date.parse(e.target.value)) {
setDateError('過去の時間を指定することはできません。');
return;
}
setDateError(null);
}, [updateDraft]);
const canPost = draft.text.length > 0 && draft.text.length <= 3000 && !dateError;
return (
<VStack>
@ -138,11 +156,12 @@ export const NoteSchedulerForm: React.FC<NoteSchedulerFormProp> = (p) => {
<InputLabel>
{t('datetime')}
<Input type="datetime-local" value={draft.dateString} disabled={p.disabled} onChange={e => updateDraft('dateString', e.target.value)}></Input>
<Input type="datetime-local" value={draft.dateString} disabled={p.disabled} error={dateError != null} onChange={onChangeDate}></Input>
{dateError && <Text color="danger" fontSize="xs"><i className="ti ti-alert-circle-filled"/> {dateError}</Text>}
</InputLabel>
<HStack gap="s" justifyContent="right" alignItems="center">
<Button primaryGradient disabled={p.disabled} onClick={() => p.onSubmit?.(draft)}>
<Button primaryGradient disabled={p.disabled || !canPost} onClick={() => p.onSubmit?.(draft)}>
{t('_noteScheduler.schedule')}
</Button>
</HStack>

View File

@ -0,0 +1,98 @@
import * as $ from '@radix-ui/react-alert-dialog';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { HStack } from '../layouts/HStack';
import { Button } from './Button';
import { styled, keyframes } from '@/libs/stitches';
export type AlertProp = {
open: boolean;
onOpenChange: (state: boolean) => void;
title?: string;
description: string;
cancelText?: string;
okText?: string;
danger?: boolean;
onOkClick?: () => void;
};
export const Alert: React.FC<AlertProp> = (p) => {
const { t } = useTranslation();
return (
<$.Root open={p.open} onOpenChange={p.onOpenChange}>
<$.Portal>
<AlertDialogOverlay />
<AlertDialogContent>
{p.title && <AlertDialogTitle>{p.title}</AlertDialogTitle>}
<AlertDialogDescription>
{p.description}
</AlertDialogDescription>
<HStack justifyContent="right">
<$.Cancel asChild>
<Button flat>
{p.cancelText ?? t('cancel')}
</Button>
</$.Cancel>
<$.Action asChild>
<Button danger={p.danger} primaryGradient={!p.danger} onClick={p.onOkClick}>
{p.okText ?? t('ok')}
</Button>
</$.Action>
</HStack>
</AlertDialogContent>
</$.Portal>
</$.Root>
);
};
const overlayShow = keyframes({
'0%': { opacity: 0 },
'100%': { opacity: 1 },
});
const contentShow = keyframes({
'0%': { opacity: 0, transform: 'translate(-50%, -48%) scale(.96)' },
'100%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' },
});
const AlertDialogOverlay = styled($.Overlay, {
backgroundColor: '$overlayBg',
position: 'fixed',
inset: 0,
animation: `${overlayShow} 0.2s $timingFunction$default`,
});
const AlertDialogContent = styled($.Content, {
position: 'fixed',
top: '50%',
left: '50%',
width: '90vw',
maxWidth: '500px',
maxHeight: '85vh',
backgroundColor: '$card',
color: '$fg',
borderRadius: '$2',
boxShadow: '$l',
transform: 'translate(-50%, -50%)',
padding: '$m $l',
animation: `${contentShow} 0.2s $timingFunction$default`,
'&:focus': { outline: 'none' },
});
const AlertDialogTitle = styled($.Title, {
margin: 0,
color: '$fg',
fontSize: '$xl',
fontWeight: 'bold',
paddingBottom: '$xs',
});
const AlertDialogDescription = styled($.Description, {
color: '$muted',
fontSize: '$m',
});

View File

@ -94,7 +94,7 @@ export const Button = styled('button', {
},
size: {
small: {
padding: '$xxs $s',
padding: '$2xs $s',
fontSize: '$xs',
},
medium: {
@ -131,5 +131,22 @@ export const Button = styled('button', {
},
},
},
{
danger: true,
flat: true,
css: {
color: '$danger',
borderColor: 'transparent',
background: 'transparent',
'&:not(:disabled):hover': {
background: '$flatHover',
borderColor: 'transparent',
},
'&:not(:disabled):active': {
background: '$flatActive',
borderColor: 'transparent',
},
},
},
],
});

View File

@ -10,7 +10,7 @@ export const Input = styled('input', {
color: '$fg',
fontSize: '$m',
border: '1px solid $divider',
transition: 'all 0.2s $timingFunction$default',
transition: 'all 0.2s ',
'&:focus': {
borderColor: '$primary',

View File

@ -33,6 +33,7 @@
"ok": "OK",
"yes": "はい",
"no": "いいえ",
"cancel": "キャンセル",
"termsOfService": "利用規約",
"resetToDefault": "初期値に戻す",
"error": "エラー",
@ -188,7 +189,9 @@
"_noteScheduler": {
"createNew": "新規作成",
"edit": "編集",
"delete": "削除",
"noNotes": "予約投稿はまだ作成されていません。",
"deleteConfirm": "本当に予約投稿「{{note}}」を削除しますか?",
"accountToNote": "投稿するアカウント",
"schedule": "投稿を予約"
}

View File

@ -46,6 +46,7 @@ export const {
switchBg: olive.olive7,
switchBgOn: '$primary',
switchThumb: olive.olive1,
overlayBg: blackA.blackA9,
},
space: {
none: '0',
@ -123,6 +124,7 @@ export const darkTheme = createTheme('dark', {
flatHover: whiteA.whiteA6,
flatActive: whiteA.whiteA5,
switchBg: oliveDark.olive8,
overlayBg: whiteA.whiteA9,
},
});

View File

@ -0,0 +1,60 @@
import dayjs from 'dayjs';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router';
import { useParams } from 'react-router-dom';
import type { Draft } from '@/components/domains/note-scheduler/NoteSchedulerForm';
import { NoteSchedulerForm } from '@/components/domains/note-scheduler/NoteSchedulerForm';
import { PageRoot } from '@/components/PageRoot';
import { Heading } from '@/components/primitives/Heading';
import { trpc } from '@/libs/trpc';
const NoteSchedulerEditPage: React.FC = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { mutateAsync: createAsync, isLoading: isCreating } = trpc.scheduleNote.create.useMutation();
const { mutateAsync: deleteAsync, isLoading: isDeleting } = trpc.scheduleNote.delete.useMutation();
const [list] = trpc.scheduleNote.list.useSuspenseQuery();
const { id } = useParams();
const note = list.find(n => n.id === id);
if (!id || !note) throw new Error('no such note');
const onSubmit = async (draft: Draft) => {
await deleteAsync(id);
await createAsync({
note: {
cw: draft.isCwEnabled ? draft.cw : null,
localOnly: draft.localOnly,
text: draft.text,
visibility: draft.visibility as any,
visibleUserIds: [],
},
timestamp: dayjs.tz(draft.dateString).utc().toDate(),
sessionId: draft.sessionId,
});
navigate('/apps/note-scheduler');
};
const initialNote: Draft = {
text: note.text,
isCwEnabled: note.cw != null,
cw: note.cw ?? '',
dateString: dayjs(note.date).tz().format('YYYY-MM-DDTHH:mm'),
visibility: note.visibility,
localOnly: note.localOnly,
sessionId: note.misskeySessionId,
};
return (
<PageRoot slim title={`${t('noteScheduler')} - ${t('_noteScheduler.edit')}`}>
<Heading as="h1">{t('_noteScheduler.edit')}</Heading>
<NoteSchedulerForm disabled={isCreating || isDeleting} initialNote={initialNote} onSubmit={onSubmit} />
</PageRoot>
);
};
export default NoteSchedulerEditPage;

View File

@ -1,14 +1,132 @@
import React from 'react';
import dayjs from 'dayjs';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import type { RouterOutput } from '@/libs/trpc';
import { HStack } from '@/components/layouts/HStack';
import { VStack } from '@/components/layouts/VStack';
import { PageRoot } from '@/components/PageRoot';
import { Alert } from '@/components/primitives/Alert';
import { Button } from '@/components/primitives/Button';
import { Heading } from '@/components/primitives/Heading';
import { Text } from '@/components/primitives/Text';
import { styled } from '@/libs/stitches';
import { trpc } from '@/libs/trpc';
const NoteCardContainer = styled('div', {
padding: '$m',
borderRadius: '$3',
background: '$card',
color: '$fg',
boxShadow: '$s',
small: {
color: '$muted',
},
variants: {
pale: {
true: {
background: '$cardPale',
boxShadow: 'none',
},
},
},
});
const Header = styled('div', {
display: 'flex',
color: '$muted',
});
const Visibility = styled('div', {
marginLeft: 'auto',
});
const MarginRight = styled('div', {
marginRight: '$2xs',
});
const NoteCard: React.FC<{ note: RouterOutput['scheduleNote']['list'][number] }> = ({ note }) => {
const [deleteConfirmDialogOpened, setDeleteConfirmDialogOpened] = useState(false);
const { t } = useTranslation();
const [sessions] = trpc.account.getMisskeySessions.useSuspenseQuery();
const { mutateAsync: deleteAsync } = trpc.scheduleNote.delete.useMutation();
const [_, { refetch }] = trpc.scheduleNote.list.useSuspenseQuery();
const date = dayjs(note.date);
const session = sessions.find(s => s.id === note.misskeySessionId);
const deleteConfirm = () => {
setDeleteConfirmDialogOpened(true);
};
const del = async () => {
await deleteAsync(note.id);
refetch();
};
const sent = date.isBefore(dayjs());
return (
<NoteCardContainer pale={sent}>
<VStack>
<Header>
<span>@{session?.username}@{session?.host}</span>
<Visibility>
<i className={visibilityIcon[note.visibility]} />
{note.localOnly && <Text as="i" color="danger" className="ti ti-rocket-off" />}
</Visibility>
</Header>
{note.cw ? (
<details>
<summary>{note.cw}</summary>
{note.text}
</details>
) : <div>{note.text}</div>}
<Text as="aside" color="muted" fontSize="s">
<MarginRight as="i" className="ti ti-report" />
<time dateTime={note.date}>{date.format('YYYY/MM/DD HH:mm')}</time>
<span>
{' '}
{ sent ? '送信済み' : '送信予定' }
</span>
</Text>
<HStack gap="xs" justifyContent="right">
{!sent && (
<Button as={Link} to={`/apps/note-scheduler/edit/${note.id}`} flat primary>
<MarginRight as="i" className="ti ti-edit" /> {t('_noteScheduler.edit')}
</Button>
)}
<Button flat danger onClick={deleteConfirm}>
<MarginRight as="i" className="ti ti-trash" /> {t('_noteScheduler.delete')}
</Button>
</HStack>
</VStack>
<Alert
danger
description={t('_noteScheduler.deleteConfirm', { note: note.text })}
open={deleteConfirmDialogOpened}
onOpenChange={setDeleteConfirmDialogOpened}
onOkClick={del}
/>
</NoteCardContainer>
);
};
const visibilityIcon: Record<string, string> = {
public: 'ti ti-world',
home: 'ti ti-home',
followers: 'ti ti-lock',
specified: 'ti ti-mail',
};
const NoteSchedulerPage: React.FC = () => {
const [notes] = trpc.scheduleNote.list.useSuspenseQuery();
const { t } = useTranslation();
@ -27,7 +145,7 @@ const NoteSchedulerPage: React.FC = () => {
<Text as="p" color="muted">
稿
</Text>
) : (notes.map(n => <p key={n.id}>{n.text}</p>))}
) : (notes.map(n => <NoteCard note={n} key={n.id} />))}
</VStack>
</section>
</PageRoot>

View File

@ -6,10 +6,10 @@
@font-face {
font-family: "Koruri";
src: url("./fonts/koruri-regular.eot");
src: url("./fonts/koruri-regular.eot?#iefix") format("embedded-opentype"),
url("./fonts/koruri-regular.woff") format("woff"),
url("./fonts/koruri-regular.ttf") format("truetype");
src: url("/fonts/koruri-regular.eot");
src: url("/fonts/koruri-regular.eot?#iefix") format("embedded-opentype"),
url("/fonts/koruri-regular.woff") format("woff"),
url("/fonts/koruri-regular.ttf") format("truetype");
font-style: normal;
font-weight: 400;
font-display: swap;
@ -17,10 +17,10 @@
@font-face {
font-family: "Koruri";
src: url("./fonts/koruri-semibold.eot");
src: url("./fonts/koruri-semibold.eot?#iefix") format("embedded-opentype"),
url("./fonts/koruri-semibold.woff") format("woff"),
url("./fonts/koruri-semibold.ttf") format("truetype");
src: url("/fonts/koruri-semibold.eot");
src: url("/fonts/koruri-semibold.eot?#iefix") format("embedded-opentype"),
url("/fonts/koruri-semibold.woff") format("woff"),
url("/fonts/koruri-semibold.ttf") format("truetype");
font-style: normal;
font-weight: 500;
font-display: swap;
@ -28,10 +28,10 @@
@font-face {
font-family: "Koruri";
src: url("./fonts/koruri-bold.eot");
src: url("./fonts/koruri-bold.eot?#iefix") format("embedded-opentype"),
url("./fonts/koruri-bold.woff") format("woff"),
url("./fonts/koruri-bold.ttf") format("truetype");
src: url("/fonts/koruri-bold.eot");
src: url("/fonts/koruri-bold.eot?#iefix") format("embedded-opentype"),
url("/fonts/koruri-bold.woff") format("woff"),
url("/fonts/koruri-bold.ttf") format("truetype");
font-style: normal;
font-weight: 700;
font-display: swap;

View File

@ -5,8 +5,8 @@ importers:
.:
devDependencies:
turbo:
specifier: ^1.9.3
version: 1.9.3
specifier: ^1.9.9
version: 1.9.9
packages/backend:
dependencies:
@ -152,6 +152,9 @@ importers:
'@radix-ui/colors':
specifier: ^0.1.8
version: 0.1.8
'@radix-ui/react-alert-dialog':
specifier: ^1.0.3
version: 1.0.3(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.4
version: 2.0.4(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
@ -266,13 +269,13 @@ importers:
version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/builder-vite':
specifier: ^7.0.7
version: 7.0.7(typescript@5.0.4)(vite@4.3.3)
version: 7.0.7(typescript@5.0.4)(vite@4.3.8)
'@storybook/react':
specifier: ^7.0.7
version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)
'@storybook/react-vite':
specifier: ^7.0.7
version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.3)
version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.8)
'@storybook/testing-library':
specifier: ^0.1.0
version: 0.1.0
@ -296,7 +299,7 @@ importers:
version: 0.4.0
'@vitejs/plugin-react':
specifier: ^4.0.0
version: 4.0.0(vite@4.3.3)
version: 4.0.0(vite@4.3.8)
eslint-config-tools:
specifier: workspace:*
version: link:../eslint-config
@ -322,11 +325,11 @@ importers:
specifier: 5.0.4
version: 5.0.4
vite:
specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
specifier: ^4.3.8
version: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
vite-tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0(typescript@5.0.4)(vite@4.3.3)
version: 4.2.0(typescript@5.0.4)(vite@4.3.8)
vitest:
specifier: ^0.30.1
version: 0.30.1(sass@1.62.1)
@ -2224,7 +2227,7 @@ packages:
'@types/yargs': 17.0.24
chalk: 4.1.2
/@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.0.4)(vite@4.3.3):
/@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.0.4)(vite@4.3.8):
resolution: {integrity: sha512-ou4ZJSXMMWHqGS4g8uNRbC5TiTWxAgQZiVucoUrOCWuPrTbkpJbmVyIi9jU72SBry7gQtuMEDp4YR8EEXAg7VQ==}
peerDependencies:
typescript: '>= 4.3.x'
@ -2238,7 +2241,7 @@ packages:
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.0.4)
typescript: 5.0.4
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
dev: true
/@jridgewell/gen-mapping@0.3.3:
@ -2405,6 +2408,25 @@ packages:
'@babel/runtime': 7.21.5
dev: false
/@radix-ui/react-alert-dialog@1.0.3(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-QXFy7+bhGi0u+paF2QbJeSCHZs4gLMJIPm6sajUamyW0fro6g1CaSGc5zmc4QmK2NlSGUrq8m+UsUqJYtzvXow==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@babel/runtime': 7.21.5
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
'@radix-ui/react-context': 1.0.0(react@18.2.0)
'@radix-ui/react-dialog': 1.0.3(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.1(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
/@radix-ui/react-arrow@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==}
peerDependencies:
@ -2450,6 +2472,33 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-dialog@1.0.3(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@babel/runtime': 7.21.5
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
'@radix-ui/react-context': 1.0.0(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.0(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.0(react@18.2.0)
'@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.1(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0)
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.0)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
/@radix-ui/react-direction@1.0.0(react@18.2.0):
resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==}
peerDependencies:
@ -3158,7 +3207,7 @@ packages:
- supports-color
dev: true
/@storybook/builder-vite@7.0.7(typescript@5.0.4)(vite@4.3.3):
/@storybook/builder-vite@7.0.7(typescript@5.0.4)(vite@4.3.8):
resolution: {integrity: sha512-2wL6fsFWzij+R155urOLc7EjZtlVWf4FLfaSlLGAuZwRQU40N04YdMaHMp9tjd9Vdr5fxEDwTB51PnVWJMlsEw==}
peerDependencies:
'@preact/preset-vite': '*'
@ -3194,7 +3243,7 @@ packages:
remark-slug: 6.1.0
rollup: 3.21.2
typescript: 5.0.4
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
dev: true
@ -3536,7 +3585,7 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/@storybook/react-vite@7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.3):
/@storybook/react-vite@7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.8):
resolution: {integrity: sha512-RuWfP/kiLpuHdcF9dWUUp9SOGMmO0FJ0HGV5yAOhGmi8KmTzvc8zjC+hJjj+sSgn2n71BO8pG/zqGl16FwfwVQ==}
engines: {node: '>=16'}
peerDependencies:
@ -3544,17 +3593,17 @@ packages:
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
vite: ^3.0.0 || ^4.0.0
dependencies:
'@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.0.4)(vite@4.3.3)
'@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.0.4)(vite@4.3.8)
'@rollup/pluginutils': 4.2.1
'@storybook/builder-vite': 7.0.7(typescript@5.0.4)(vite@4.3.3)
'@storybook/builder-vite': 7.0.7(typescript@5.0.4)(vite@4.3.8)
'@storybook/react': 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)
'@vitejs/plugin-react': 3.1.0(vite@4.3.3)
'@vitejs/plugin-react': 3.1.0(vite@4.3.8)
ast-types: 0.14.2
magic-string: 0.27.0
react: 18.2.0
react-docgen: 6.0.0-alpha.3
react-dom: 18.2.0(react@18.2.0)
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- '@preact/preset-vite'
- supports-color
@ -4218,7 +4267,7 @@ packages:
eslint-visitor-keys: 3.4.0
dev: true
/@vitejs/plugin-react@3.1.0(vite@4.3.3):
/@vitejs/plugin-react@3.1.0(vite@4.3.8):
resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@ -4229,12 +4278,12 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5)
magic-string: 0.27.0
react-refresh: 0.14.0
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
dev: true
/@vitejs/plugin-react@4.0.0(vite@4.3.3):
/@vitejs/plugin-react@4.0.0(vite@4.3.8):
resolution: {integrity: sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@ -4244,7 +4293,7 @@ packages:
'@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5)
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5)
react-refresh: 0.14.0
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
dev: true
@ -11072,65 +11121,65 @@ packages:
typescript: 5.0.4
dev: true
/turbo-darwin-64@1.9.3:
resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==}
/turbo-darwin-64@1.9.9:
resolution: {integrity: sha512-UDGM9E21eCDzF5t1F4rzrjwWutcup33e7ZjNJcW/mJDPorazZzqXGKEPIy9kXwKhamUUXfC7668r6ZuA1WXF2Q==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-darwin-arm64@1.9.3:
resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==}
/turbo-darwin-arm64@1.9.9:
resolution: {integrity: sha512-VyfkXzTJpYLTAQ9krq2myyEq7RPObilpS04lgJ4OO1piq76RNmSpX9F/t9JCaY9Pj/4TL7i0d8PM7NGhwEA5Ag==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-linux-64@1.9.3:
resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==}
/turbo-linux-64@1.9.9:
resolution: {integrity: sha512-Fu1MY29Odg8dHOqXcpIIGC3T63XLOGgnGfbobXMKdrC7JQDvtJv8TUCYciRsyknZYjyyKK1z6zKuYIiDjf3KeQ==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-linux-arm64@1.9.3:
resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==}
/turbo-linux-arm64@1.9.9:
resolution: {integrity: sha512-50LI8NafPuJxdnMCBeDdzgyt1cgjQG7FwkyY336v4e95WJPUVjrHdrKH6jYXhOUyrv9+jCJxwX1Yrg02t5yJ1g==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-windows-64@1.9.3:
resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==}
/turbo-windows-64@1.9.9:
resolution: {integrity: sha512-9IsTReoLmQl1IRsy3WExe2j2RKWXQyXujfJ4fXF+jp08KxjVF4/tYP2CIRJx/A7UP/7keBta27bZqzAjsmbSTA==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo-windows-arm64@1.9.3:
resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==}
/turbo-windows-arm64@1.9.9:
resolution: {integrity: sha512-CUu4hpeQo68JjDr0V0ygTQRLbS+/sNfdqEVV+Xz9136vpKn2WMQLAuUBVZV0Sp0S/7i+zGnplskT0fED+W46wQ==}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo@1.9.3:
resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==}
/turbo@1.9.9:
resolution: {integrity: sha512-+ZS66LOT7ahKHxh6XrIdcmf2Yk9mNpAbPEj4iF2cs0cAeaDU3xLVPZFF0HbSho89Uxwhx7b5HBgPbdcjQTwQkg==}
hasBin: true
requiresBuild: true
optionalDependencies:
turbo-darwin-64: 1.9.3
turbo-darwin-arm64: 1.9.3
turbo-linux-64: 1.9.3
turbo-linux-arm64: 1.9.3
turbo-windows-64: 1.9.3
turbo-windows-arm64: 1.9.3
turbo-darwin-64: 1.9.9
turbo-darwin-arm64: 1.9.9
turbo-linux-64: 1.9.9
turbo-linux-arm64: 1.9.9
turbo-windows-64: 1.9.9
turbo-windows-arm64: 1.9.9
dev: true
/twemoji-parser@14.0.0:
@ -11537,7 +11586,7 @@ packages:
mlly: 1.2.0
pathe: 1.1.0
picocolors: 1.0.0
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- '@types/node'
- less
@ -11548,7 +11597,7 @@ packages:
- terser
dev: true
/vite-tsconfig-paths@4.2.0(typescript@5.0.4)(vite@4.3.3):
/vite-tsconfig-paths@4.2.0(typescript@5.0.4)(vite@4.3.8):
resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==}
peerDependencies:
vite: '*'
@ -11559,14 +11608,14 @@ packages:
debug: 4.3.4
globrex: 0.1.2
tsconfck: 2.1.1(typescript@5.0.4)
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/vite@4.3.3(@types/node@18.16.3)(sass@1.62.1):
resolution: {integrity: sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==}
/vite@4.3.8(@types/node@18.16.3)(sass@1.62.1):
resolution: {integrity: sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@ -11653,7 +11702,7 @@ packages:
strip-literal: 1.0.1
tinybench: 2.5.0
tinypool: 0.4.0
vite: 4.3.3(@types/node@18.16.3)(sass@1.62.1)
vite: 4.3.8(@types/node@18.16.3)(sass@1.62.1)
vite-node: 0.30.1(@types/node@18.16.3)(sass@1.62.1)
why-is-node-running: 2.2.2
transitivePeerDependencies: