## New Feature: Full Screen Slide Preview - Add SlidePreviewModal component for full screen slide preview in SlideEditor - ESC key support and temporary hint for user guidance - Proper aspect ratio handling with theme CSS inheritance - Modal follows existing UI patterns for consistency ## Import Standards Compliance (31 files updated) - Fix all imports to use explicit .tsx/.ts extensions per IMPORT_STANDARDS.md - Eliminate barrel imports in App.tsx for better Vite tree shaking - Add direct imports with explicit paths across entire codebase - Preserve CSS imports and external library imports unchanged ## Code Architecture Improvements - Add comprehensive CSS & Component Architecture Guidelines to CLAUDE.md - Document modal patterns, aspect ratio handling, and CSS reuse principles - Reference all coding standards files for consistent development workflow - Prevent future CSS overcomplication and component duplication ## Performance Optimizations - Enable Vite tree shaking with proper import structure - Improve module resolution speed with explicit extensions - Optimize build performance through direct imports ## Files Changed - 31 TypeScript/React files with import fixes - 2 new SlidePreviewModal files (component + CSS) - Updated project documentation and coding guidelines - Fixed aspect ratio CSS patterns across components 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
179 lines
3.7 KiB
TypeScript
179 lines
3.7 KiB
TypeScript
import React from 'react';
|
|
import { Modal } from './Modal.tsx';
|
|
|
|
interface ConfirmDialogProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onConfirm: () => void;
|
|
title?: string;
|
|
message: string;
|
|
type?: 'info' | 'warning' | 'danger';
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
isDestructive?: boolean;
|
|
}
|
|
|
|
export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
|
isOpen,
|
|
onClose,
|
|
onConfirm,
|
|
title,
|
|
message,
|
|
type = 'info',
|
|
confirmText,
|
|
cancelText = 'Cancel',
|
|
isDestructive = false
|
|
}) => {
|
|
const getIcon = () => {
|
|
switch (type) {
|
|
case 'danger':
|
|
return '⚠️';
|
|
case 'warning':
|
|
return '⚠️';
|
|
default:
|
|
return '❓';
|
|
}
|
|
};
|
|
|
|
const getTitle = () => {
|
|
if (title) return title;
|
|
|
|
switch (type) {
|
|
case 'danger':
|
|
return 'Confirm Deletion';
|
|
case 'warning':
|
|
return 'Confirm Action';
|
|
default:
|
|
return 'Confirm';
|
|
}
|
|
};
|
|
|
|
const getConfirmText = () => {
|
|
if (confirmText) return confirmText;
|
|
|
|
switch (type) {
|
|
case 'danger':
|
|
return 'Delete';
|
|
case 'warning':
|
|
return 'Continue';
|
|
default:
|
|
return 'Confirm';
|
|
}
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
onConfirm();
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
size="small"
|
|
title={getTitle()}
|
|
closeOnOverlayClick={false}
|
|
>
|
|
<div className="confirm-dialog">
|
|
<div className="confirm-content">
|
|
<div className="confirm-icon">{getIcon()}</div>
|
|
<p className="confirm-message">{message}</p>
|
|
</div>
|
|
<div className="confirm-actions">
|
|
<button
|
|
type="button"
|
|
className="button secondary"
|
|
onClick={onClose}
|
|
>
|
|
{cancelText}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`button ${isDestructive || type === 'danger' ? 'danger' : 'primary'}`}
|
|
onClick={handleConfirm}
|
|
autoFocus
|
|
>
|
|
{getConfirmText()}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<style jsx>{`
|
|
.confirm-dialog {
|
|
text-align: center;
|
|
}
|
|
|
|
.confirm-content {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.confirm-icon {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.confirm-message {
|
|
color: #374151;
|
|
font-size: 1rem;
|
|
line-height: 1.5;
|
|
margin: 0;
|
|
text-align: left;
|
|
}
|
|
|
|
.confirm-actions {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.button {
|
|
padding: 0.75rem 1.5rem;
|
|
border-radius: 0.5rem;
|
|
font-weight: 500;
|
|
font-size: 0.875rem;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
min-width: 80px;
|
|
}
|
|
|
|
.button.primary {
|
|
background: #3b82f6;
|
|
color: white;
|
|
}
|
|
|
|
.button.primary:hover {
|
|
background: #2563eb;
|
|
}
|
|
|
|
.button.secondary {
|
|
background: #f3f4f6;
|
|
color: #374151;
|
|
border: 1px solid #d1d5db;
|
|
}
|
|
|
|
.button.secondary:hover {
|
|
background: #e5e7eb;
|
|
}
|
|
|
|
.button.danger {
|
|
background: #dc2626;
|
|
color: white;
|
|
}
|
|
|
|
.button.danger:hover {
|
|
background: #b91c1c;
|
|
}
|
|
|
|
.button:focus {
|
|
outline: 2px solid #3b82f6;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.button.danger:focus {
|
|
outline-color: #dc2626;
|
|
}
|
|
`}</style>
|
|
</Modal>
|
|
);
|
|
}; |