Skip to content

Commit 0e70063

Browse files
committed
feat: add PadsDialog for managing pads
- Introduced a new PadsDialog component to facilitate pad management, including renaming and deleting pads. - Updated ExcalidrawWrapper and MainMenuConfig to integrate the new PadsDialog, allowing users to access it from the main menu. - Enhanced the Tabs component to handle active pad changes via custom events, improving synchronization across components. - Added styles for the PadsDialog to ensure a consistent and user-friendly interface. - Implemented analytics tracking for pad creation, renaming, and deletion events to enhance user insights.
1 parent 0ab5d9a commit 0e70063

File tree

6 files changed

+590
-55
lines changed

6 files changed

+590
-55
lines changed

src/frontend/src/ExcalidrawWrapper.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { MainMenuConfig } from './ui/MainMenu';
88
import { lockEmbeddables, renderCustomEmbeddable } from './CustomEmbeddableRenderer';
99
import AuthDialog from './ui/AuthDialog';
1010
import BackupsModal from './ui/BackupsDialog';
11+
import PadsDialog from './ui/PadsDialog';
1112
import SettingsDialog from './ui/SettingsDialog';
1213
import { capture } from './utils/posthog';
1314
import { Footer } from '@atyrode/excalidraw';
@@ -53,6 +54,7 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
5354

5455
// State for modals
5556
const [showBackupsModal, setShowBackupsModal] = useState(false);
57+
const [showPadsModal, setShowPadsModal] = useState(false);
5658
const [showSettingsModal, setShowSettingsModal] = useState(false);
5759

5860
// Handle auth state changes
@@ -69,6 +71,10 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
6971
setShowBackupsModal(false);
7072
};
7173

74+
const handleClosePadsModal = () => {
75+
setShowPadsModal(false);
76+
};
77+
7278
const handleCloseSettingsModal = () => {
7379
setShowSettingsModal(false);
7480
};
@@ -119,6 +125,8 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
119125
excalidrawAPI={excalidrawAPI}
120126
showBackupsModal={showBackupsModal}
121127
setShowBackupsModal={setShowBackupsModal}
128+
showPadsModal={showPadsModal}
129+
setShowPadsModal={setShowPadsModal}
122130
showSettingsModal={showSettingsModal}
123131
setShowSettingsModal={setShowSettingsModal}
124132
/>
@@ -135,6 +143,13 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
135143
/>
136144
)}
137145

146+
{showPadsModal && (
147+
<PadsDialog
148+
excalidrawAPI={excalidrawAPI}
149+
onClose={handleClosePadsModal}
150+
/>
151+
)}
152+
138153
{showSettingsModal && (
139154
<SettingsDialog
140155
excalidrawAPI={excalidrawAPI}

src/frontend/src/ui/MainMenu.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
33
import type { ExcalidrawImperativeAPI } from '@atyrode/excalidraw/types';
44
import type { MainMenu as MainMenuType } from '@atyrode/excalidraw';
55

6-
import { LogOut, SquarePlus, LayoutDashboard, SquareCode, Eye, Coffee, Grid2x2, User, Text, ArchiveRestore, Settings, Terminal } from 'lucide-react';
6+
import { LogOut, SquarePlus, LayoutDashboard, SquareCode, Eye, Coffee, Grid2x2, User, Text, ArchiveRestore, Settings, Terminal, FileText } from 'lucide-react';
77
import AccountDialog from './AccountDialog';
88
import md5 from 'crypto-js/md5';
99
import { capture } from '../utils/posthog';
@@ -22,6 +22,8 @@ interface MainMenuConfigProps {
2222
excalidrawAPI: ExcalidrawImperativeAPI | null;
2323
showBackupsModal: boolean;
2424
setShowBackupsModal: (show: boolean) => void;
25+
showPadsModal: boolean;
26+
setShowPadsModal: (show: boolean) => void;
2527
showSettingsModal?: boolean;
2628
setShowSettingsModal?: (show: boolean) => void;
2729
}
@@ -30,6 +32,7 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
3032
MainMenu,
3133
excalidrawAPI,
3234
setShowBackupsModal,
35+
setShowPadsModal,
3336
setShowSettingsModal = (show: boolean) => {},
3437
}) => {
3538
const [showAccountModal, setShowAccountModal] = useState(false);
@@ -128,6 +131,10 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
128131
const handleCanvasBackupsClick = () => {
129132
setShowBackupsModal(true);
130133
};
134+
135+
const handleManagePadsClick = () => {
136+
setShowPadsModal(true);
137+
};
131138

132139
const handleSettingsClick = () => {
133140
setShowSettingsModal(true);
@@ -230,6 +237,12 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
230237
<MainMenu.DefaultItems.LoadScene />
231238
<MainMenu.DefaultItems.Export />
232239
<MainMenu.DefaultItems.SaveAsImage />
240+
<MainMenu.Item
241+
icon={<FileText />}
242+
onClick={handleManagePadsClick}
243+
>
244+
Manage pads...
245+
</MainMenu.Item>
233246
<MainMenu.Item
234247
icon={<ArchiveRestore />}
235248
onClick={handleCanvasBackupsClick}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
.pads-dialog {
2+
&__wrapper {
3+
position: fixed;
4+
top: 0;
5+
left: 0;
6+
right: 0;
7+
bottom: 0;
8+
z-index: 1000;
9+
}
10+
11+
&__title-container {
12+
display: flex;
13+
align-items: center;
14+
justify-content: space-between;
15+
}
16+
17+
&__title {
18+
margin: 0;
19+
font-size: 1.2rem;
20+
font-weight: 600;
21+
}
22+
23+
&__content {
24+
padding: 1rem;
25+
max-height: 70vh;
26+
overflow-y: auto;
27+
}
28+
29+
&__loading,
30+
&__error,
31+
&__empty {
32+
text-align: center;
33+
padding: 2rem 0;
34+
color: var(--text-primary-color);
35+
}
36+
37+
&__list {
38+
list-style: none;
39+
padding: 0;
40+
margin: 0;
41+
}
42+
43+
&__item {
44+
display: flex;
45+
justify-content: space-between;
46+
align-items: center;
47+
padding: 0.75rem;
48+
border-radius: 6px;
49+
margin-bottom: 0.5rem;
50+
background-color: var(--island-bg-color);
51+
transition: background-color 0.2s ease;
52+
53+
&:hover {
54+
background-color: var(--button-hover-bg);
55+
}
56+
57+
&--active {
58+
border-left: 3px solid #cc6d24;
59+
}
60+
}
61+
62+
&__item-content {
63+
display: flex;
64+
flex-direction: column;
65+
flex: 1;
66+
padding: 0.25rem;
67+
border-radius: 4px;
68+
transition: background-color 0.2s ease;
69+
70+
&--clickable {
71+
cursor: pointer;
72+
73+
&:hover {
74+
background-color: var(--button-hover-bg);
75+
}
76+
}
77+
78+
&--current {
79+
cursor: default;
80+
}
81+
}
82+
83+
&__name {
84+
font-weight: 500;
85+
margin-bottom: 0.25rem;
86+
}
87+
88+
&__current {
89+
font-weight: normal;
90+
font-style: italic;
91+
color: #cc6d24;
92+
}
93+
94+
&__timestamps {
95+
display: flex;
96+
flex-direction: column;
97+
gap: 0.2rem;
98+
}
99+
100+
&__timestamp {
101+
font-size: 0.8rem;
102+
color: var(--text-secondary-color);
103+
}
104+
105+
&__actions {
106+
display: flex;
107+
gap: 0.5rem;
108+
}
109+
110+
&__icon-button {
111+
display: flex;
112+
align-items: center;
113+
justify-content: center;
114+
background: none;
115+
border: none;
116+
border-radius: 4px;
117+
padding: 0.4rem;
118+
cursor: pointer;
119+
color: var(--text-primary-color);
120+
transition: background-color 0.2s ease, color 0.2s ease;
121+
122+
&:hover {
123+
background-color: var(--button-hover-bg);
124+
color: #cc6d24;
125+
}
126+
127+
&:disabled {
128+
opacity: 0.5;
129+
cursor: not-allowed;
130+
131+
&:hover {
132+
background: none;
133+
color: var(--text-primary-color);
134+
}
135+
}
136+
}
137+
138+
&__edit-form {
139+
display: flex;
140+
flex-direction: column;
141+
width: 100%;
142+
gap: 0.5rem;
143+
144+
input {
145+
padding: 0.5rem;
146+
border-radius: 4px;
147+
border: 1px solid var(--button-gray-2);
148+
background-color: var(--input-bg-color);
149+
color: var(--text-primary-color);
150+
font-size: 1rem;
151+
}
152+
}
153+
154+
&__edit-actions {
155+
display: flex;
156+
gap: 0.5rem;
157+
}
158+
159+
&__button {
160+
padding: 0.4rem 0.8rem;
161+
border-radius: 4px;
162+
border: none;
163+
cursor: pointer;
164+
font-size: 0.9rem;
165+
transition: background-color 0.2s ease;
166+
167+
&--save {
168+
background-color: #cc6d24;
169+
color: white;
170+
171+
&:hover {
172+
background-color: #b05e1f;
173+
}
174+
}
175+
176+
&--cancel {
177+
background-color: var(--button-gray-2);
178+
color: var(--text-primary-color);
179+
180+
&:hover {
181+
background-color: var(--button-gray-3);
182+
}
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)