cetirizine

An experimental matrix client written in reactjs utilizing tailwind and storybook
git clone git://archive.git.mtrnord.blog/MTRNord/cetirizine.git
Log | Files | Refs | README | LICENSE

commit 211ceccb9f5bbaea5ef2f4083bfc48f02245f75b
parent bc2f4589e5403a06408d1256b0c4603d7aad98b1
Author: MTRNord <mtrnord1@gmail.com>
Date:   Fri, 21 Apr 2023 21:34:19 +0200

feat: Add avatar component and cleanup stories

Diffstat:
Mpackage-lock.json | 214++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mpackage.json | 2+-
Asrc/components/avatar/avatar.stories.tsx | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/avatar/avatar.tsx | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/button/button.stories.tsx | 23-----------------------
Msrc/components/header/header.stories.tsx | 5-----
Msrc/components/input/basic/input.stories.tsx | 7+------
Msrc/components/input/chat/input.stories.tsx | 17++++++++---------
Msrc/components/input/chat/input.tsx | 4++--
Msrc/pages/LoginPage.stories.tsx | 10++--------
Mvite.config.ts | 2+-
11 files changed, 364 insertions(+), 68 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -41,7 +41,7 @@ "@storybook/react-vite": "^7.0.6", "@storybook/testing-library": "^0.0.14-next.2", "@tailwindcss/forms": "^0.5.3", - "@vitejs/plugin-react": "^4.0.0-beta.0", + "@vitejs/plugin-react-swc": "^3.3.0", "autoprefixer": "^10.4.14", "postcss": "^8.4.23", "postcss-loader": "^7.2.4", @@ -4973,6 +4973,200 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@swc/core": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.53.tgz", + "integrity": "sha512-OM5nCfKDZXr1HjxD072Jlx5463tPX7xeY7NDSRE3X4KFlkRDFdyMWAyV3pet1oouOfUNrzzoVTAR4XSU8ytO6Q==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.53", + "@swc/core-darwin-x64": "1.3.53", + "@swc/core-linux-arm-gnueabihf": "1.3.53", + "@swc/core-linux-arm64-gnu": "1.3.53", + "@swc/core-linux-arm64-musl": "1.3.53", + "@swc/core-linux-x64-gnu": "1.3.53", + "@swc/core-linux-x64-musl": "1.3.53", + "@swc/core-win32-arm64-msvc": "1.3.53", + "@swc/core-win32-ia32-msvc": "1.3.53", + "@swc/core-win32-x64-msvc": "1.3.53" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.53.tgz", + "integrity": "sha512-JvWwV/duzdQ60iwWYceDhDk75LmdrLoPC7myX3Src3gl/bJtETMq7uHS9uY8m0GQOqbct7XGR3q5Ff21YxkSzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.53.tgz", + "integrity": "sha512-UuIGZtCfUPJM2Q01bRIFzmucOMg8UZ+mY3kh5xB8kl/VrLltBlraSWGjjJzYmUeUxiF8+CtMfeSYav5QfU2v3g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.53.tgz", + "integrity": "sha512-LupAjTErteyLmowYIfiQeTz3uVh7/SPYv/EuG1PYrajNoUYomt7WA0rQUoyglF9VtwVyNqxptWEO5So32ApTHA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.53.tgz", + "integrity": "sha512-kREfZdiJH/O8GtJJ22wVN9DVzz/+CPAkw5Mn5te2KQg0xJHMWaESU5XeYMWvtwyOQVmb31b6zCGFy3pnBWWfGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.53.tgz", + "integrity": "sha512-VeAgomBr6BVuBRjZjRHmvp5gKp1nZgbbd441ca1AvsPd2c+ZyhyHLxTWeHOzBDa/vYnmi9BCwx3QJzFqbAFPVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.53.tgz", + "integrity": "sha512-LFX5+QpQkESPkmx860C40pIiYf1utEqoA+WDtmKnUz3DucYvw3eGlXCBdyklP7UBWwJktKIcPlIqr7yROY5VlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.53.tgz", + "integrity": "sha512-O0lbJgeaM0VEsG8wFYvpF+Iuf0IENv+LnXHoygkAsv67sVW54+gFxav2sEdkftD5qYe9ku4tmtTVYRZlFgC84Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.53.tgz", + "integrity": "sha512-7PgvPl0aNLaFZSK+rIi4DB1g0aW2qOsTIJQSJGRszsCP8pze/traXymyuSG2I3y9Hx7Z+bP5ycJydyAgCw88WA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.53.tgz", + "integrity": "sha512-T+OacGm69t8+1mt1sHlwhREiFiFgSeIGL3h11FIs8o2zKnOr5z2H9myzR432X8WuHGVQAOCMvDu53LCMBD0ZzQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.53", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.53.tgz", + "integrity": "sha512-uV1/GhROJ/SXzj+f+kKcVtR2GuAiggvbqepzZS46+G47okf6229hr2T1fjmiwYyA75w9R3Bj/wil4UhodohOLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@tailwindcss/forms": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", @@ -5626,22 +5820,16 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, - "node_modules/@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.0.tgz", + "integrity": "sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==", "dev": true, "dependencies": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "@swc/core": "^1.3.42" }, "peerDependencies": { - "vite": "^4.2.0" + "vite": "^4" } }, "node_modules/@webassemblyjs/ast": { diff --git a/package.json b/package.json @@ -72,7 +72,7 @@ "@storybook/react-vite": "^7.0.6", "@storybook/testing-library": "^0.0.14-next.2", "@tailwindcss/forms": "^0.5.3", - "@vitejs/plugin-react": "^4.0.0-beta.0", + "@vitejs/plugin-react-swc": "^3.3.0", "autoprefixer": "^10.4.14", "postcss": "^8.4.23", "postcss-loader": "^7.2.4", diff --git a/src/components/avatar/avatar.stories.tsx b/src/components/avatar/avatar.stories.tsx @@ -0,0 +1,95 @@ +import { Meta, StoryObj } from '@storybook/react'; +import Avatar from './avatar'; +import { BADGE } from '@geometricpanda/storybook-addon-badges'; + +const meta: Meta<typeof Avatar> = { + title: 'Chat/Avatar', + tags: ['autodocs'], + component: Avatar, + parameters: { + badges: [BADGE.EXPERIMENTAL] + }, + argTypes: { + avatarUrl: { + required: false, + name: "Avatar URL", + description: "The URL of the avatar image", + }, + userID: { + required: true, + name: "User ID", + description: "The DISPLAY name of the User", + }, + dm: { + required: false, + defaultValue: false, + control: "boolean", + name: "DM", + description: "Wether the Avatar is used for a DM", + }, + online: { + required: false, + defaultValue: false, + control: "boolean", + name: "Online", + description: "Wether the user is online. Only used if dm is true", + }, + } +}; + +export default meta; +type Story = StoryObj<typeof Avatar>; + +export const Fallback: Story = { + args: { + userID: "test", + dm: false, + online: false, + avatarUrl: "" + } +}; + +export const Image: Story = { + args: { + userID: "test", + avatarUrl: "https://randomuser.me/api/portraits/men/62.jpg", + dm: false, + online: false, + } +}; + +export const DMOffline: Story = { + args: { + userID: "test", + avatarUrl: "https://randomuser.me/api/portraits/men/62.jpg", + dm: true, + online: false, + } +}; + +export const DMOnline: Story = { + args: { + userID: "test", + avatarUrl: "https://randomuser.me/api/portraits/men/62.jpg", + dm: true, + online: true, + } +}; + +export const DMNoAvatarOffline: Story = { + args: { + userID: "test", + avatarUrl: "", + dm: true, + online: false, + } +}; + +export const DMNoAvatarOnline: Story = { + args: { + userID: "test", + avatarUrl: "", + dm: true, + online: true, + } +}; +\ No newline at end of file diff --git a/src/components/avatar/avatar.tsx b/src/components/avatar/avatar.tsx @@ -0,0 +1,51 @@ +type AvatarProps = { + /** + * The URL of the Avatar image + */ + avatarUrl?: string + /** + * The displayname of the avatar user + */ + userID: string + /** + * Wether it is a DM or not + */ + dm: boolean + /** + * Wether the user is online. Only used if dm is true. + */ + online: boolean +}; + +export default function Avatar({ avatarUrl, userID, dm = false, online = false }: AvatarProps) { + if (avatarUrl) { + return ( + <div className="flex relative w-12 h-12 justify-center items-center m-1 mr-2 text-xl rounded-full text-white"> + <img className="rounded-full" alt={userID} src={avatarUrl} /> + { + dm ? + ( + online ? + <div className="bg-green-500 rounded-full w-3 h-3 absolute bottom-[2px] right-[2px]"></div> : + <div className="bg-red-500 rounded-full w-3 h-3 absolute bottom-[2px] right-[2px]"></div> + ) : + <></> + } + </div> + ); + } + return ( + <div className="flex relative w-12 h-12 bg-orange-500 justify-center items-center m-1 mr-2 text-xl rounded-full text-white"> + {userID[0].toUpperCase()} + { + dm ? + ( + online ? + <div className="bg-green-500 rounded-full w-3 h-3 absolute bottom-[2px] right-[2px]"></div> : + <div className="bg-red-500 rounded-full w-3 h-3 absolute bottom-[2px] right-[2px]"></div> + ) : + <></> + } + </div> + ); +} +\ No newline at end of file diff --git a/src/components/button/button.stories.tsx b/src/components/button/button.stories.tsx @@ -1,6 +1,4 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { store } from '../../app/store'; import Button from './button'; import { BADGE } from '@geometricpanda/storybook-addon-badges'; @@ -45,9 +43,6 @@ export const Default: Story = { type: 'button', style: 'primary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const Submit: Story = { @@ -56,9 +51,6 @@ export const Submit: Story = { type: 'submit', style: 'primary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const Reset: Story = { @@ -67,9 +59,6 @@ export const Reset: Story = { type: 'reset', style: 'primary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const DefaultSecondary: Story = { @@ -78,9 +67,6 @@ export const DefaultSecondary: Story = { type: 'button', style: 'secondary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const SubmitSecondary: Story = { @@ -89,9 +75,6 @@ export const SubmitSecondary: Story = { type: 'submit', style: 'secondary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const ResetSecondary: Story = { @@ -100,9 +83,6 @@ export const ResetSecondary: Story = { type: 'reset', style: 'secondary', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; export const Abort: Story = { @@ -111,7 +91,4 @@ export const Abort: Story = { type: 'button', style: 'abort', }, - render: (args) => <Provider store={store}> - <Button {...args} /> - </Provider>, }; \ No newline at end of file diff --git a/src/components/header/header.stories.tsx b/src/components/header/header.stories.tsx @@ -1,6 +1,4 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { store } from '../../app/store'; import Header from './header'; import { BADGE } from '@geometricpanda/storybook-addon-badges'; @@ -27,7 +25,4 @@ export const Default: Story = { args: { children: 'Header', }, - render: (args) => <Provider store={store}> - <Header {...args} /> - </Provider>, }; \ No newline at end of file diff --git a/src/components/input/basic/input.stories.tsx b/src/components/input/basic/input.stories.tsx @@ -1,6 +1,4 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { store } from '../../../app/store'; import Input from './input'; import { BADGE } from '@geometricpanda/storybook-addon-badges'; @@ -53,8 +51,5 @@ export const Default: Story = { autoFocus: false, value: "", onChange: (_) => { } - }, - render: (args) => <Provider store={store}> - <Input {...args} /> - </Provider>, + } }; \ No newline at end of file diff --git a/src/components/input/chat/input.stories.tsx b/src/components/input/chat/input.stories.tsx @@ -1,10 +1,9 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { store } from '../../../app/store'; import ChatInput from './input'; import { BADGE } from '@geometricpanda/storybook-addon-badges'; -import { $getRoot, EditorState } from 'lexical'; +import { $getRoot, EditorState, LexicalEditor } from 'lexical'; import { TRANSFORMERS, $convertToMarkdownString } from "@lexical/markdown"; +import { $generateHtmlFromNodes } from "@lexical/html"; const meta: Meta<typeof ChatInput> = { title: 'Chat/Input', @@ -35,19 +34,19 @@ type Story = StoryObj<typeof ChatInput>; export const Default: Story = { args: { - onChange: (editorState: EditorState) => { + onChange: (editorState: EditorState, editor: LexicalEditor) => { editorState.read(() => { // Read the contents of the EditorState here. const root = $getRoot(); // make it markdown const markdown = $convertToMarkdownString(TRANSFORMERS, root); + const html = $generateHtmlFromNodes(editor, null); console.log(markdown); + console.log(html); }); }, - onError: (error) => { console.error(error); } - }, - render: (args) => <Provider store={store}> - <ChatInput {...args} /> - </Provider>, + onError: (error) => { console.error(error); }, + namespace: "Editor" + } }; \ No newline at end of file diff --git a/src/components/input/chat/input.tsx b/src/components/input/chat/input.tsx @@ -38,7 +38,7 @@ type ChatInputProps = { }; function Placeholder() { - return <div className="editor-placeholder">Enter some rich text...</div>; + return <div className="editor-placeholder" id="editor-placeholder">Enter message...</div>; } export default function ChatInput({ namespace, onChange, onError }: ChatInputProps) { @@ -66,7 +66,7 @@ export default function ChatInput({ namespace, onChange, onError }: ChatInputPro <ToolbarPlugin /> <div className="editor-inner"> <RichTextPlugin - contentEditable={<ContentEditable className="editor-input" />} + contentEditable={<ContentEditable className="editor-input" aria-labelledby='editor-placeholder' />} placeholder={<Placeholder />} ErrorBoundary={LexicalErrorBoundary} /> diff --git a/src/pages/LoginPage.stories.tsx b/src/pages/LoginPage.stories.tsx @@ -1,6 +1,4 @@ import { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { store } from '../app/store'; import LoginPage from './LoginPage'; const meta: Meta<typeof LoginPage> = { @@ -12,8 +10,4 @@ const meta: Meta<typeof LoginPage> = { export default meta; type Story = StoryObj<typeof LoginPage>; -export const Default: Story = { - render: () => <Provider store={store}> - <LoginPage /> - </Provider>, -}; -\ No newline at end of file +export const Default: Story = {}; +\ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts @@ -1,5 +1,5 @@ import { defineConfig, splitVendorChunkPlugin } from 'vite' -import react from '@vitejs/plugin-react' +import react from "@vitejs/plugin-react-swc"; // https://vitejs.dev/config/ export default defineConfig({