Files
esengine/packages/editor/editor-app/src/components/Toast.tsx
YHH 155411e743 refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
2025-12-26 14:50:35 +08:00

89 lines
2.7 KiB
TypeScript

import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
import { CheckCircle, XCircle, AlertCircle, Info, X } from 'lucide-react';
import '../styles/Toast.css';
export type ToastType = 'success' | 'error' | 'warning' | 'info';
export interface Toast {
id: string;
message: string;
type: ToastType;
duration?: number;
}
interface ToastContextValue {
showToast: (message: string, type?: ToastType, duration?: number) => void;
hideToast: (id: string) => void;
}
const ToastContext = createContext<ToastContextValue | null>(null);
export const useToast = () => {
const context = useContext(ToastContext);
if (!context) {
throw new Error('useToast must be used within ToastProvider');
}
return context;
};
interface ToastProviderProps {
children: ReactNode;
}
export const ToastProvider: React.FC<ToastProviderProps> = ({ children }) => {
const [toasts, setToasts] = useState<Toast[]>([]);
const showToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {
const id = `toast-${Date.now()}-${Math.random()}`;
const toast: Toast = { id, message, type, duration };
setToasts((prev) => [...prev, toast]);
if (duration > 0) {
setTimeout(() => {
hideToast(id);
}, duration);
}
}, []);
const hideToast = useCallback((id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
const getIcon = (type: ToastType) => {
switch (type) {
case 'success':
return <CheckCircle size={20} />;
case 'error':
return <XCircle size={20} />;
case 'warning':
return <AlertCircle size={20} />;
case 'info':
return <Info size={20} />;
}
};
return (
<ToastContext.Provider value={{ showToast, hideToast }}>
{children}
<div className="toast-container">
{toasts.map((toast) => (
<div key={toast.id} className={`toast toast-${toast.type}`}>
<div className="toast-icon">
{getIcon(toast.type)}
</div>
<div className="toast-message">{toast.message}</div>
<button
className="toast-close"
onClick={() => hideToast(toast.id)}
aria-label="关闭"
>
<X size={16} />
</button>
</div>
))}
</div>
</ToastContext.Provider>
);
};