Skip to content

Commit

Permalink
[126] emojis and other improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pro100Koss committed Nov 8, 2024
1 parent 98d5ea8 commit a79ed30
Show file tree
Hide file tree
Showing 22 changed files with 387 additions and 25 deletions.
3 changes: 3 additions & 0 deletions frontend/spa/.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ VITE_KEYCLOAK_URL=VITE_KEYCLOAK_URL
VITE_KEYCLOAK_REALM=VITE_KEYCLOAK_REALM
VITE_KEYCLOAK_CLIENT_ID=VITE_KEYCLOAK_CLIENT_ID

# API
REACT_APP_API_URL=REACT_APP_API_URL

4 changes: 4 additions & 0 deletions frontend/spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@
"sbom": "cdxgen -o bom.json"
},
"dependencies": {
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@minoru/react-dnd-treeview": "^3.4.4",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@types/lodash": "^4.17.11",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"emoji-mart": "^5.6.0",
"eslint-plugin-import": "^2.31.0",
"i18next": "^23.16.0",
"keycloak-js": "^24.0.5",
Expand Down
69 changes: 69 additions & 0 deletions frontend/spa/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions frontend/spa/src/@types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ declare global {
const onTestFailed: (typeof import('vitest'))['onTestFailed'];
const onTestFinished: (typeof import('vitest'))['onTestFinished'];
}

declare namespace JSX {
interface IntrinsicElements {
'em-emoji': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & { id: string };
}
}

export {};
2 changes: 2 additions & 0 deletions frontend/spa/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { useEffect, useState } from 'react';
import Layout from '@/layouts/default/Layout.tsx';
import themeService from '@/services/ThemeService.ts';
import authService from './services/auth/AuthService';
import emojiService from './services/EmojiService';

function App() {
const [isInitialized, setIsInitialized] = useState(false);

useEffect(() => {
themeService.init();
authService.init().catch((e) => console.error('Failed to init authService:', e));
emojiService.init().catch((e) => console.error('Failed to init emojiService:', e));

setIsInitialized(true);
}, []);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Tree, getBackendOptions, MultiBackend, NodeModel } from '@minoru/react-dnd-treeview';
import { Tree, getBackendOptions, MultiBackend } from '@minoru/react-dnd-treeview';
import { DndProvider } from 'react-dnd';
import { useState } from 'react';

Expand All @@ -7,17 +7,18 @@ import OrganizationTreeDropPreview from '@/components/OrganizationStructureTree/
import OrganizationTreePlaceholder from '@/components/OrganizationStructureTree/OrganizationTreePlaceholder.tsx';
import Button from '@/components/ui/Button.tsx';
import { getMaxId } from '@/components/OrganizationStructureTree/OrganizationTreeUtils';
import { OrganizationTreeNodeModel } from '@/components/OrganizationStructureTree/OrganizationTreeNodeModel.ts';

interface OrganizationStructureTreeProps {
tree: NodeModel[];
onTreeChanged?: (tree: NodeModel[]) => void;
tree: OrganizationTreeNodeModel[];
onTreeChanged?: (tree: OrganizationTreeNodeModel[]) => void;
}

function OrganizationTree({ tree, onTreeChanged }: OrganizationStructureTreeProps) {
const [treeData, setTreeData] = useState<NodeModel[]>(JSON.parse(JSON.stringify(tree)));
const [treeData, setTreeData] = useState<OrganizationTreeNodeModel[]>(JSON.parse(JSON.stringify(tree)));
const [isDragging, setIsDragging] = useState(false);

const handleDrop = (newTree: NodeModel[]) => {
const handleDrop = (newTree: OrganizationTreeNodeModel[]) => {
setTreeData(newTree);
onTreeChanged?.(newTree);
};
Expand All @@ -27,13 +28,13 @@ function OrganizationTree({ tree, onTreeChanged }: OrganizationStructureTreeProp
onTreeChanged?.(newTree);
};

const onDeleteNode = (id: NodeModel['id']) => {
const onDeleteNode = (id: OrganizationTreeNodeModel['id']) => {
const newTree = treeData.filter((node) => node.id !== id);
setTreeData(newTree);
onTreeChanged?.(newTree);
};

const onEditNode = (id: NodeModel['id'], text: string) => {
const onEditNode = (id: OrganizationTreeNodeModel['id'], text: string) => {
const newTree = treeData.map((node) => (node.id === id ? { ...node, text } : node));
setTreeData(newTree);
onTreeChanged?.(newTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import Icon from '@/components/ui/Icon.tsx';
import TextField from '@/components/ui/TextField.tsx';
import Button from '@/components/ui/Button.tsx';
import { cn } from '@/lib/utils.ts';
import EmojiField from '@/components/ui/EmojiField.tsx';
import { OrganizationTreeNodeModel } from '@/components/OrganizationStructureTree/OrganizationTreeNodeModel.ts';
import Emoji from '@/components/ui/Emoji.tsx';

type Props = {
node: NodeModel;
node: OrganizationTreeNodeModel;
depth: number;
isOpen: boolean;
hasChild: boolean;
Expand All @@ -17,11 +20,14 @@ type Props = {
onDelete: (id: NodeModel['id']) => void;
};

const IDENT_SIZE = 32;

function OrganizationTreeNode(props: Props) {
const { id } = props.node;
const indent = props.depth * 16;
const { id, data } = props.node;
const indent = props.depth * IDENT_SIZE;
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState('');
const [emoji, setEmoji] = useState(data?.emoji || '');
const [isHover, setIsHover] = useState(false);
const textFieldRef = useRef<HTMLInputElement>(null);

Expand All @@ -34,6 +40,10 @@ function OrganizationTreeNode(props: Props) {
setEditValue(value);
};

const onEmojiChaged = (value: string) => {
setEmoji(value);
};

const onClickEdit = () => {
setEditValue(props.node.text);
setIsEditing(true);
Expand Down Expand Up @@ -88,12 +98,22 @@ function OrganizationTreeNode(props: Props) {
<div className="w-full">
{isEditing ? (
<div className="flex flex-row items-center w-full">
<TextField ref={textFieldRef} value={editValue} className="py-1 px-1 w-full" onChange={onEditValueChange} onKeyDown={onKeyDown} />
<EmojiField value={emoji} className="py-0 px-1 h-8" onChange={onEmojiChaged} />
<TextField
ref={textFieldRef}
value={editValue}
className="py-1 px-1 h-8 w-full"
onChange={onEditValueChange}
onKeyDown={onKeyDown}
/>
<Button variant="link" className="px-1" icon="Check" onClick={onClickAcceptEdit} />
<Button variant="link" className="px-1" icon="Cross1" onClick={onClickCancelEdit} />
</div>
) : (
<div className="flex flex-row items-center">
<span className="mr-1">
<Emoji emoji={emoji} className="text-xl" />
</span>
{props.node.text}
<div className="flex-grow"></div>
{isHover && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { NodeModel } from '@minoru/react-dnd-treeview';

type TreeData = {
emoji: string;
};

export type OrganizationTreeNodeModel = NodeModel<TreeData>;
5 changes: 4 additions & 1 deletion frontend/spa/src/components/SettingsPage/SettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useTranslation } from 'react-i18next';

import SettingsPageMenu from '@/components/SettingsPage/SettingsPageMenu.tsx';
import Icon from '@/components/ui/Icon.tsx';
import { MenuItem } from '@/components/SettingsPage/MenuItem.ts';
Expand All @@ -11,6 +13,7 @@ interface SettingsPageProps {
}

function SettingsPage({ activeTab, menu, children, onActiveTabChanged }: SettingsPageProps) {
const { t } = useTranslation();
const activeItem = menu.find((item) => item.value === activeTab);
const onChangeTab = (value: string) => {
if (onActiveTabChanged) {
Expand All @@ -25,7 +28,7 @@ function SettingsPage({ activeTab, menu, children, onActiveTabChanged }: Setting
<div className="px-4 pt-4">
<h1 className="flex flex-row gap-2 h-10 items-center">
<Icon icon="HamburgerMenu" size="md" />
Menu
{t('settingsPage.menu')}
</h1>

<div className="py-4">
Expand Down
14 changes: 14 additions & 0 deletions frontend/spa/src/components/ui/Emoji.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { cn } from '@/lib/utils.ts';

interface EmojiProps {
emoji: string;
className?: string;
}

function Emoji({ emoji, className }: EmojiProps) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return <em-emoji id={emoji} class={cn(className)}></em-emoji>;
}

export default Emoji;
Loading

0 comments on commit a79ed30

Please sign in to comment.