@qwanyx/stack 0.2.66 → 0.2.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.ts +120 -2
- package/dist/components/AuthFlow.d.ts +57 -0
- package/dist/components/AuthProvider.d.ts +26 -0
- package/dist/components/ComboStack.d.ts +5 -1
- package/dist/components/ForgotPasswordForm.d.ts +32 -0
- package/dist/components/LoginForm.d.ts +50 -0
- package/dist/components/QMap/QMap.d.ts +14 -0
- package/dist/components/QMap/QMapEdge.d.ts +21 -0
- package/dist/components/QMap/QMapEdgeLayer.d.ts +17 -0
- package/dist/components/QMap/QMapNode.d.ts +24 -0
- package/dist/components/QMap/QMapSelectionBox.d.ts +19 -0
- package/dist/components/QMap/hooks/useNodeDrag.d.ts +17 -0
- package/dist/components/QMap/hooks/useSelection.d.ts +17 -0
- package/dist/components/QMap/hooks/useZoomPan.d.ts +19 -0
- package/dist/components/QMap/index.d.ts +15 -0
- package/dist/components/QMap/types.d.ts +158 -0
- package/dist/components/QMap/utils/geometry.d.ts +70 -0
- package/dist/components/RegisterForm.d.ts +42 -0
- package/dist/components/ResetPasswordForm.d.ts +33 -0
- package/dist/components/VerifyCodeForm.d.ts +38 -0
- package/dist/components/VoiceTextEditor.d.ts +24 -0
- package/dist/index.cjs.js +72 -45
- package/dist/index.d.ts +21 -1
- package/dist/index.esm.js +14735 -10316
- package/package.json +5 -1
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Handles token storage and
|
|
2
|
+
* Authentication Module
|
|
3
|
+
* Handles token storage, user management, and API authentication
|
|
4
4
|
*/
|
|
5
|
+
export interface AuthUser {
|
|
6
|
+
_id: string;
|
|
7
|
+
email: string;
|
|
8
|
+
role: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}
|
|
11
|
+
export interface LoginResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
user?: AuthUser;
|
|
14
|
+
token?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
requiresVerification?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface RegisterResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
userId?: string;
|
|
21
|
+
email?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
requiresVerification?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface VerifyEmailResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
user?: AuthUser;
|
|
28
|
+
token?: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface PasswordResetResult {
|
|
32
|
+
success: boolean;
|
|
33
|
+
message?: string;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface AuthServiceConfig {
|
|
37
|
+
apiUrl: string;
|
|
38
|
+
systemId?: string;
|
|
39
|
+
onLogin?: (user: AuthUser) => void;
|
|
40
|
+
onLogout?: () => void;
|
|
41
|
+
}
|
|
5
42
|
export declare class AuthManager {
|
|
6
43
|
/**
|
|
7
44
|
* Store authentication token
|
|
@@ -31,4 +68,85 @@ export declare class AuthManager {
|
|
|
31
68
|
* Get authorization header
|
|
32
69
|
*/
|
|
33
70
|
static getAuthHeader(): Record<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Store user data
|
|
73
|
+
*/
|
|
74
|
+
static setUser(user: AuthUser): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get stored user data
|
|
77
|
+
*/
|
|
78
|
+
static getUser(): AuthUser | null;
|
|
79
|
+
/**
|
|
80
|
+
* Clear user data
|
|
81
|
+
*/
|
|
82
|
+
static clearUser(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Clear all auth data
|
|
85
|
+
*/
|
|
86
|
+
static clearAll(): void;
|
|
87
|
+
}
|
|
88
|
+
export declare class AuthService {
|
|
89
|
+
private config;
|
|
90
|
+
constructor(config: AuthServiceConfig);
|
|
91
|
+
/**
|
|
92
|
+
* Register a new user with email and password
|
|
93
|
+
* Sends verification code to email
|
|
94
|
+
*/
|
|
95
|
+
register(email: string, password: string, options?: {
|
|
96
|
+
firstName?: string;
|
|
97
|
+
lastName?: string;
|
|
98
|
+
phone?: string;
|
|
99
|
+
}): Promise<RegisterResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Verify email with code received by email
|
|
102
|
+
* Returns token on success (auto-login)
|
|
103
|
+
*/
|
|
104
|
+
verifyEmail(email: string, code: string): Promise<VerifyEmailResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Request password reset code sent to email
|
|
107
|
+
*/
|
|
108
|
+
requestPasswordReset(email: string): Promise<PasswordResetResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Set new password using verification code
|
|
111
|
+
* Returns token on success (auto-login)
|
|
112
|
+
*/
|
|
113
|
+
setPasswordWithCode(email: string, code: string, password: string): Promise<VerifyEmailResult>;
|
|
114
|
+
/**
|
|
115
|
+
* Login with email and password
|
|
116
|
+
* Uses system coprocessor for authentication
|
|
117
|
+
*/
|
|
118
|
+
login(email: string, password: string): Promise<LoginResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Resend verification code to email
|
|
121
|
+
* (Uses same endpoint as password reset)
|
|
122
|
+
*/
|
|
123
|
+
resendVerificationCode(email: string): Promise<PasswordResetResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Logout - clear all auth data
|
|
126
|
+
*/
|
|
127
|
+
logout(): void;
|
|
128
|
+
/**
|
|
129
|
+
* Check if user has a specific role in a group
|
|
130
|
+
*/
|
|
131
|
+
checkRole(groupId: string, userId: string): Promise<string | null>;
|
|
132
|
+
/**
|
|
133
|
+
* Check if user is admin for current system
|
|
134
|
+
*/
|
|
135
|
+
isAdmin(userId: string): Promise<boolean>;
|
|
136
|
+
/**
|
|
137
|
+
* Get current user from storage
|
|
138
|
+
*/
|
|
139
|
+
getCurrentUser(): AuthUser | null;
|
|
140
|
+
/**
|
|
141
|
+
* Check if user is authenticated
|
|
142
|
+
*/
|
|
143
|
+
isAuthenticated(): boolean;
|
|
34
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Initialize the auth service
|
|
147
|
+
*/
|
|
148
|
+
export declare function initializeAuthService(config: AuthServiceConfig): AuthService;
|
|
149
|
+
/**
|
|
150
|
+
* Get the auth service instance
|
|
151
|
+
*/
|
|
152
|
+
export declare function getAuthService(): AuthService;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { LoginFormTheme } from './LoginForm';
|
|
3
|
+
export type AuthFlowState = 'login' | 'register' | 'verify-email' | 'forgot-password' | 'verify-reset-code' | 'reset-password';
|
|
4
|
+
export interface AuthFlowTheme extends LoginFormTheme {
|
|
5
|
+
}
|
|
6
|
+
export interface AuthFlowProps {
|
|
7
|
+
onLogin: (email: string, password: string) => Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
requiresVerification?: boolean;
|
|
11
|
+
}>;
|
|
12
|
+
onRegister: (email: string, password: string, options?: {
|
|
13
|
+
firstName?: string;
|
|
14
|
+
lastName?: string;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
success: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
requiresVerification?: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
onVerifyEmail: (email: string, code: string) => Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}>;
|
|
24
|
+
onRequestPasswordReset: (email: string) => Promise<{
|
|
25
|
+
success: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
}>;
|
|
28
|
+
onResetPassword: (email: string, code: string, password: string) => Promise<{
|
|
29
|
+
success: boolean;
|
|
30
|
+
error?: string;
|
|
31
|
+
}>;
|
|
32
|
+
onResendCode?: (email: string) => Promise<{
|
|
33
|
+
success: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
}>;
|
|
36
|
+
onSuccess?: () => void;
|
|
37
|
+
initialState?: AuthFlowState;
|
|
38
|
+
logo?: ReactNode;
|
|
39
|
+
theme?: AuthFlowTheme;
|
|
40
|
+
showNameFields?: boolean;
|
|
41
|
+
className?: string;
|
|
42
|
+
isModal?: boolean;
|
|
43
|
+
labels?: {
|
|
44
|
+
loginTitle?: string;
|
|
45
|
+
registerTitle?: string;
|
|
46
|
+
verifyTitle?: string;
|
|
47
|
+
forgotPasswordTitle?: string;
|
|
48
|
+
resetPasswordTitle?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export declare function AuthFlow({ onLogin, onRegister, onVerifyEmail, onRequestPasswordReset, onResetPassword, onResendCode, onSuccess, initialState, logo, theme, showNameFields, className, isModal, labels, }: AuthFlowProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
export interface AuthFlowModalProps extends AuthFlowProps {
|
|
53
|
+
isOpen: boolean;
|
|
54
|
+
onClose: () => void;
|
|
55
|
+
backdropBlur?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export declare function AuthFlowModal({ isOpen, onClose, backdropBlur, ...flowProps }: AuthFlowModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { AuthUser, LoginResult, RegisterResult, VerifyEmailResult, PasswordResetResult } from '../auth';
|
|
3
|
+
export interface AuthContextType {
|
|
4
|
+
user: AuthUser | null;
|
|
5
|
+
token: string | null;
|
|
6
|
+
isAdmin: boolean;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
login: (email: string, password: string) => Promise<LoginResult>;
|
|
9
|
+
register: (email: string, password: string, options?: {
|
|
10
|
+
firstName?: string;
|
|
11
|
+
lastName?: string;
|
|
12
|
+
}) => Promise<RegisterResult>;
|
|
13
|
+
verifyEmail: (email: string, code: string) => Promise<VerifyEmailResult>;
|
|
14
|
+
requestPasswordReset: (email: string) => Promise<PasswordResetResult>;
|
|
15
|
+
setPasswordWithCode: (email: string, code: string, password: string) => Promise<VerifyEmailResult>;
|
|
16
|
+
resendVerificationCode: (email: string) => Promise<PasswordResetResult>;
|
|
17
|
+
logout: () => void;
|
|
18
|
+
refreshUser: () => void;
|
|
19
|
+
}
|
|
20
|
+
export interface AuthProviderConfig {
|
|
21
|
+
apiUrl: string;
|
|
22
|
+
systemId: string;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export declare function AuthProvider({ apiUrl, systemId, children }: AuthProviderConfig): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function useAuth(): AuthContextType;
|
|
@@ -12,12 +12,16 @@ export interface ComboStackOption {
|
|
|
12
12
|
export interface ComboStackProps {
|
|
13
13
|
/** Available options */
|
|
14
14
|
options: ComboStackOption[];
|
|
15
|
+
/** Currently selected value (id) */
|
|
16
|
+
value?: string;
|
|
15
17
|
/** Type of items (for create modal) */
|
|
16
18
|
type?: 'project' | 'contact' | 'folder' | 'context' | 'focus' | string;
|
|
17
19
|
/** Placeholder text for search */
|
|
18
20
|
placeholder?: string;
|
|
19
21
|
/** Called when an item is clicked */
|
|
20
22
|
onItemClick?: (option: ComboStackOption) => void;
|
|
23
|
+
/** Called when selection is cleared */
|
|
24
|
+
onClear?: () => void;
|
|
21
25
|
/** Called when + is clicked to create new item */
|
|
22
26
|
onCreate?: () => void;
|
|
23
27
|
/** Max height before scrolling (default: 300px) */
|
|
@@ -36,4 +40,4 @@ export interface ComboStackTheme {
|
|
|
36
40
|
border?: string;
|
|
37
41
|
primary?: string;
|
|
38
42
|
}
|
|
39
|
-
export declare function ComboStack({ options, type, placeholder, onItemClick, onCreate, maxHeight, className, theme: themeProp, }: ComboStackProps): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
export declare function ComboStack({ options, value, type, placeholder, onItemClick, onClear, onCreate, maxHeight, className, theme: themeProp, }: ComboStackProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ForgotPasswordFormTheme {
|
|
2
|
+
primaryColor?: string;
|
|
3
|
+
primaryHoverColor?: string;
|
|
4
|
+
backgroundColor?: string;
|
|
5
|
+
textColor?: string;
|
|
6
|
+
textMutedColor?: string;
|
|
7
|
+
borderColor?: string;
|
|
8
|
+
errorColor?: string;
|
|
9
|
+
errorBackgroundColor?: string;
|
|
10
|
+
successColor?: string;
|
|
11
|
+
successBackgroundColor?: string;
|
|
12
|
+
inputBackground?: string;
|
|
13
|
+
borderRadius?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ForgotPasswordFormProps {
|
|
16
|
+
onRequestReset: (email: string) => Promise<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
onSuccess?: (email: string) => void;
|
|
21
|
+
onBackClick?: () => void;
|
|
22
|
+
title?: string;
|
|
23
|
+
subtitle?: string;
|
|
24
|
+
submitText?: string;
|
|
25
|
+
loadingText?: string;
|
|
26
|
+
emailLabel?: string;
|
|
27
|
+
backText?: string;
|
|
28
|
+
theme?: ForgotPasswordFormTheme;
|
|
29
|
+
className?: string;
|
|
30
|
+
isModal?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function ForgotPasswordForm({ onRequestReset, onSuccess, onBackClick, title, subtitle, submitText, loadingText, emailLabel, backText, theme: customTheme, className, isModal, }: ForgotPasswordFormProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface LoginFormTheme {
|
|
3
|
+
primaryColor?: string;
|
|
4
|
+
primaryHoverColor?: string;
|
|
5
|
+
backgroundColor?: string;
|
|
6
|
+
textColor?: string;
|
|
7
|
+
textMutedColor?: string;
|
|
8
|
+
borderColor?: string;
|
|
9
|
+
errorColor?: string;
|
|
10
|
+
errorBackgroundColor?: string;
|
|
11
|
+
inputBackground?: string;
|
|
12
|
+
borderRadius?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface LoginFormProps {
|
|
15
|
+
/** Function to handle login - should return { success, error? } */
|
|
16
|
+
onLogin: (email: string, password: string) => Promise<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Callback after successful login */
|
|
21
|
+
onSuccess?: () => void;
|
|
22
|
+
/** Logo element to display at top */
|
|
23
|
+
logo?: ReactNode;
|
|
24
|
+
/** Title text (default: "Connexion") */
|
|
25
|
+
title?: string;
|
|
26
|
+
/** Submit button text (default: "Se connecter") */
|
|
27
|
+
submitText?: string;
|
|
28
|
+
/** Loading text (default: "Connexion...") */
|
|
29
|
+
loadingText?: string;
|
|
30
|
+
/** Email label (default: "Email") */
|
|
31
|
+
emailLabel?: string;
|
|
32
|
+
/** Password label (default: "Mot de passe") */
|
|
33
|
+
passwordLabel?: string;
|
|
34
|
+
/** Theme customization */
|
|
35
|
+
theme?: LoginFormTheme;
|
|
36
|
+
/** Additional class name for the form container */
|
|
37
|
+
className?: string;
|
|
38
|
+
/** Whether this is displayed in a modal (adds shadow/rounding) */
|
|
39
|
+
isModal?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function LoginForm({ onLogin, onSuccess, logo, title, submitText, loadingText, emailLabel, passwordLabel, theme: customTheme, className, isModal, }: LoginFormProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export interface LoginModalProps extends LoginFormProps {
|
|
43
|
+
/** Whether the modal is open */
|
|
44
|
+
isOpen: boolean;
|
|
45
|
+
/** Callback to close the modal */
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
/** Backdrop blur (default: true) */
|
|
48
|
+
backdropBlur?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export declare function LoginModal({ isOpen, onClose, backdropBlur, ...formProps }: LoginModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMap - 2D Graph Visualization Component
|
|
3
|
+
*
|
|
4
|
+
* A powerful graph/mindmap component for visualizing and editing
|
|
5
|
+
* node-edge graphs with:
|
|
6
|
+
* - React component rendering for nodes
|
|
7
|
+
* - SVG edges (bezier, orthogonal, straight)
|
|
8
|
+
* - Zoom and pan (d3-zoom)
|
|
9
|
+
* - Multi-selection with rubber-band
|
|
10
|
+
* - Node dragging
|
|
11
|
+
*/
|
|
12
|
+
import { GraphNode } from '../../types';
|
|
13
|
+
import type { QMapProps } from './types';
|
|
14
|
+
export declare function QMap<T extends GraphNode = GraphNode>({ viewNode, nodes, edges, renderNode, defaultNodeWidth, defaultNodeHeight, onNodeMove, onNodeClick, onNodeDoubleClick, onNodeContextMenu, onSelectionChange, onEdgeClick, onViewChange, onTransformChange, minZoom, maxZoom, initialTransform, defaultEdgeStyle, defaultEdgeColor, defaultEdgeWidth, draggable, selectable, zoomable, pannable, className, style, background, showGrid, gridSize }: QMapProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMapEdge - Individual edge SVG path component
|
|
3
|
+
*
|
|
4
|
+
* Renders a single edge between two nodes with:
|
|
5
|
+
* - Configurable style (bezier, orthogonal, straight)
|
|
6
|
+
* - Configurable color and width
|
|
7
|
+
* - Click handling
|
|
8
|
+
* - Hover feedback
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import type { QMapEdge as QMapEdgeType, QMapNodePosition, EdgeStyle } from './types';
|
|
12
|
+
export interface QMapEdgeProps {
|
|
13
|
+
edge: QMapEdgeType;
|
|
14
|
+
sourcePosition: QMapNodePosition;
|
|
15
|
+
targetPosition: QMapNodePosition;
|
|
16
|
+
style: EdgeStyle;
|
|
17
|
+
color: string;
|
|
18
|
+
width: number;
|
|
19
|
+
onClick?: (edge: QMapEdgeType, event: React.MouseEvent) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare const QMapEdge: React.NamedExoticComponent<QMapEdgeProps>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMapEdgeLayer - SVG container for all edges
|
|
3
|
+
*
|
|
4
|
+
* Renders all edges in a single SVG element for performance.
|
|
5
|
+
* Positioned absolutely to overlay the node layer.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { QMapEdge as QMapEdgeType, QMapNodePosition, EdgeStyle } from './types';
|
|
9
|
+
export interface QMapEdgeLayerProps {
|
|
10
|
+
edges: QMapEdgeType[];
|
|
11
|
+
nodePositions: Map<string, QMapNodePosition>;
|
|
12
|
+
defaultStyle: EdgeStyle;
|
|
13
|
+
defaultColor: string;
|
|
14
|
+
defaultWidth: number;
|
|
15
|
+
onEdgeClick?: (edge: QMapEdgeType, event: React.MouseEvent) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const QMapEdgeLayer: React.NamedExoticComponent<QMapEdgeLayerProps>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMapNode - Individual node wrapper for QMap
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Absolute positioning based on position data
|
|
6
|
+
* - Rotation via CSS transform
|
|
7
|
+
* - Drag initiation
|
|
8
|
+
* - Selection visual state
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import type { QMapNodePosition } from './types';
|
|
12
|
+
export interface QMapNodeProps {
|
|
13
|
+
nodeId: string;
|
|
14
|
+
position: QMapNodePosition;
|
|
15
|
+
isSelected: boolean;
|
|
16
|
+
isDragging: boolean;
|
|
17
|
+
draggable: boolean;
|
|
18
|
+
onDragStart: (nodeId: string, event: React.MouseEvent) => void;
|
|
19
|
+
onClick?: (event: React.MouseEvent) => void;
|
|
20
|
+
onDoubleClick?: (event: React.MouseEvent) => void;
|
|
21
|
+
onContextMenu?: (event: React.MouseEvent) => void;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export declare const QMapNode: React.NamedExoticComponent<QMapNodeProps>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMapSelectionBox - Rubber-band selection rectangle
|
|
3
|
+
*
|
|
4
|
+
* Renders a semi-transparent selection box during marquee selection.
|
|
5
|
+
* Positioned in canvas coordinates but rendered in screen space.
|
|
6
|
+
*/
|
|
7
|
+
import type { QMapTransform } from './types';
|
|
8
|
+
export interface QMapSelectionBoxProps {
|
|
9
|
+
/** Selection box in canvas coordinates */
|
|
10
|
+
box: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
/** Current transform for converting to screen space */
|
|
17
|
+
transform: QMapTransform;
|
|
18
|
+
}
|
|
19
|
+
export declare const QMapSelectionBox: import("react").NamedExoticComponent<QMapSelectionBoxProps>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useNodeDrag - Node dragging hook for QMap
|
|
3
|
+
*
|
|
4
|
+
* Handles individual node dragging with:
|
|
5
|
+
* - Mouse down/move/up events
|
|
6
|
+
* - Transform-aware coordinate conversion
|
|
7
|
+
* - Drag state tracking
|
|
8
|
+
*/
|
|
9
|
+
import type { QMapTransform, UseNodeDragReturn } from '../types';
|
|
10
|
+
export interface UseNodeDragOptions {
|
|
11
|
+
transform: QMapTransform;
|
|
12
|
+
onDragStart?: (nodeId: string) => void;
|
|
13
|
+
onDragMove?: (nodeId: string, x: number, y: number, deltaX: number, deltaY: number) => void;
|
|
14
|
+
onDragEnd?: (nodeId: string, x: number, y: number) => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function useNodeDrag(options: UseNodeDragOptions): UseNodeDragReturn;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useSelection - Multi-selection hook for QMap
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Rubber-band (marquee) selection by dragging on background
|
|
6
|
+
* - Click to select single node
|
|
7
|
+
* - Shift+click for additive selection
|
|
8
|
+
* - Select all / clear selection
|
|
9
|
+
*/
|
|
10
|
+
import type { QMapTransform, QMapNodePosition, UseSelectionReturn } from '../types';
|
|
11
|
+
export interface UseSelectionOptions {
|
|
12
|
+
transform: QMapTransform;
|
|
13
|
+
nodePositions: Map<string, QMapNodePosition>;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
onSelectionChange?: (selectedIds: string[]) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function useSelection(options: UseSelectionOptions): UseSelectionReturn;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useZoomPan - d3-zoom integration hook for QMap
|
|
3
|
+
*
|
|
4
|
+
* Handles zoom/pan with:
|
|
5
|
+
* - Mouse wheel zoom
|
|
6
|
+
* - Touch pinch zoom
|
|
7
|
+
* - Pan by dragging background
|
|
8
|
+
* - Smooth transitions
|
|
9
|
+
*/
|
|
10
|
+
import type { QMapTransform, UseZoomPanReturn } from '../types';
|
|
11
|
+
export interface UseZoomPanOptions {
|
|
12
|
+
minZoom?: number;
|
|
13
|
+
maxZoom?: number;
|
|
14
|
+
initialTransform?: QMapTransform;
|
|
15
|
+
zoomable?: boolean;
|
|
16
|
+
pannable?: boolean;
|
|
17
|
+
onTransformChange?: (transform: QMapTransform) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function useZoomPan(options?: UseZoomPanOptions): UseZoomPanReturn;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMap - 2D Graph Visualization Component
|
|
3
|
+
*
|
|
4
|
+
* @qwanyx/stack component for visualizing and editing node-edge graphs.
|
|
5
|
+
*/
|
|
6
|
+
export { QMap } from './QMap';
|
|
7
|
+
export { QMapNode } from './QMapNode';
|
|
8
|
+
export { QMapEdge as QMapEdgeComponent } from './QMapEdge';
|
|
9
|
+
export { QMapEdgeLayer } from './QMapEdgeLayer';
|
|
10
|
+
export { QMapSelectionBox } from './QMapSelectionBox';
|
|
11
|
+
export { useZoomPan } from './hooks/useZoomPan';
|
|
12
|
+
export { useNodeDrag } from './hooks/useNodeDrag';
|
|
13
|
+
export { useSelection } from './hooks/useSelection';
|
|
14
|
+
export * from './utils/geometry';
|
|
15
|
+
export type { QMapProps, QMapNodePosition, QMapTransform, QMapViewData, QMapEdge, EdgeStyle, QMapNodeProps, QMapEdgeLayerProps, QMapEdgeProps, QMapSelectionBoxProps, UseZoomPanReturn, UseSelectionReturn, UseNodeDragReturn } from './types';
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QMap - 2D Graph Visualization Component Types
|
|
3
|
+
*/
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
import { GraphNode } from '../../types';
|
|
6
|
+
export interface QMapNodePosition {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
rotation?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface QMapTransform {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
k: number;
|
|
17
|
+
}
|
|
18
|
+
export interface QMapViewData {
|
|
19
|
+
/** Node positions keyed by node._id */
|
|
20
|
+
positions: Record<string, QMapNodePosition>;
|
|
21
|
+
/** Current viewport transform */
|
|
22
|
+
transform?: QMapTransform;
|
|
23
|
+
/** Per-edge style overrides keyed by edge id */
|
|
24
|
+
edgeStyles?: Record<string, EdgeStyle>;
|
|
25
|
+
}
|
|
26
|
+
export type EdgeStyle = 'bezier' | 'orthogonal' | 'straight';
|
|
27
|
+
export interface QMapEdge {
|
|
28
|
+
id: string;
|
|
29
|
+
source: string;
|
|
30
|
+
target: string;
|
|
31
|
+
style?: EdgeStyle;
|
|
32
|
+
label?: string;
|
|
33
|
+
color?: string;
|
|
34
|
+
width?: number;
|
|
35
|
+
data?: Record<string, any>;
|
|
36
|
+
}
|
|
37
|
+
export interface QMapProps<T extends GraphNode = GraphNode> {
|
|
38
|
+
/** Parent view node containing positions in data.positions */
|
|
39
|
+
viewNode: GraphNode;
|
|
40
|
+
/** Nodes to display on the map */
|
|
41
|
+
nodes: T[];
|
|
42
|
+
/** Edges connecting nodes */
|
|
43
|
+
edges: QMapEdge[];
|
|
44
|
+
/** Custom node renderer */
|
|
45
|
+
renderNode: (node: T, isSelected: boolean, position: QMapNodePosition) => ReactNode;
|
|
46
|
+
/** Default node dimensions when not specified in position */
|
|
47
|
+
defaultNodeWidth?: number;
|
|
48
|
+
defaultNodeHeight?: number;
|
|
49
|
+
onNodeMove?: (nodeId: string, x: number, y: number) => void;
|
|
50
|
+
onNodeClick?: (node: T, event: React.MouseEvent) => void;
|
|
51
|
+
onNodeDoubleClick?: (node: T, event: React.MouseEvent) => void;
|
|
52
|
+
onNodeContextMenu?: (node: T, event: React.MouseEvent) => void;
|
|
53
|
+
onSelectionChange?: (selectedIds: string[]) => void;
|
|
54
|
+
onEdgeClick?: (edge: QMapEdge, event: React.MouseEvent) => void;
|
|
55
|
+
onViewChange?: (viewData: QMapViewData) => void;
|
|
56
|
+
onTransformChange?: (transform: QMapTransform) => void;
|
|
57
|
+
minZoom?: number;
|
|
58
|
+
maxZoom?: number;
|
|
59
|
+
initialTransform?: QMapTransform;
|
|
60
|
+
defaultEdgeStyle?: EdgeStyle;
|
|
61
|
+
defaultEdgeColor?: string;
|
|
62
|
+
defaultEdgeWidth?: number;
|
|
63
|
+
/** Allow nodes to be dragged */
|
|
64
|
+
draggable?: boolean;
|
|
65
|
+
/** Allow multi-select with rubber-band */
|
|
66
|
+
selectable?: boolean;
|
|
67
|
+
/** Allow zoom with wheel */
|
|
68
|
+
zoomable?: boolean;
|
|
69
|
+
/** Allow pan by dragging background */
|
|
70
|
+
pannable?: boolean;
|
|
71
|
+
className?: string;
|
|
72
|
+
style?: React.CSSProperties;
|
|
73
|
+
/** Background color or pattern */
|
|
74
|
+
background?: string;
|
|
75
|
+
/** Show grid pattern */
|
|
76
|
+
showGrid?: boolean;
|
|
77
|
+
gridSize?: number;
|
|
78
|
+
}
|
|
79
|
+
export interface QMapNodeProps<T extends GraphNode = GraphNode> {
|
|
80
|
+
node: T;
|
|
81
|
+
position: QMapNodePosition;
|
|
82
|
+
isSelected: boolean;
|
|
83
|
+
isDragging: boolean;
|
|
84
|
+
transform: QMapTransform;
|
|
85
|
+
draggable: boolean;
|
|
86
|
+
onDragStart: (nodeId: string, event: React.MouseEvent) => void;
|
|
87
|
+
onDragMove: (nodeId: string, deltaX: number, deltaY: number) => void;
|
|
88
|
+
onDragEnd: (nodeId: string) => void;
|
|
89
|
+
onClick?: (node: T, event: React.MouseEvent) => void;
|
|
90
|
+
onDoubleClick?: (node: T, event: React.MouseEvent) => void;
|
|
91
|
+
onContextMenu?: (node: T, event: React.MouseEvent) => void;
|
|
92
|
+
children: ReactNode;
|
|
93
|
+
}
|
|
94
|
+
export interface QMapEdgeLayerProps {
|
|
95
|
+
edges: QMapEdge[];
|
|
96
|
+
nodePositions: Map<string, QMapNodePosition>;
|
|
97
|
+
defaultStyle: EdgeStyle;
|
|
98
|
+
defaultColor: string;
|
|
99
|
+
defaultWidth: number;
|
|
100
|
+
onEdgeClick?: (edge: QMapEdge, event: React.MouseEvent) => void;
|
|
101
|
+
}
|
|
102
|
+
export interface QMapEdgeProps {
|
|
103
|
+
edge: QMapEdge;
|
|
104
|
+
sourcePosition: QMapNodePosition;
|
|
105
|
+
targetPosition: QMapNodePosition;
|
|
106
|
+
style: EdgeStyle;
|
|
107
|
+
color: string;
|
|
108
|
+
width: number;
|
|
109
|
+
onClick?: (edge: QMapEdge, event: React.MouseEvent) => void;
|
|
110
|
+
}
|
|
111
|
+
export interface QMapSelectionBoxProps {
|
|
112
|
+
/** Selection box in canvas coordinates */
|
|
113
|
+
box: {
|
|
114
|
+
x: number;
|
|
115
|
+
y: number;
|
|
116
|
+
width: number;
|
|
117
|
+
height: number;
|
|
118
|
+
};
|
|
119
|
+
/** Current transform for converting to screen space */
|
|
120
|
+
transform: QMapTransform;
|
|
121
|
+
}
|
|
122
|
+
export interface UseZoomPanReturn {
|
|
123
|
+
transform: QMapTransform;
|
|
124
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
125
|
+
zoomTo: (x: number, y: number, scale: number, animate?: boolean) => void;
|
|
126
|
+
zoomIn: () => void;
|
|
127
|
+
zoomOut: () => void;
|
|
128
|
+
resetZoom: () => void;
|
|
129
|
+
fitToContent: (nodes: QMapNodePosition[], padding?: number) => void;
|
|
130
|
+
}
|
|
131
|
+
export interface UseSelectionReturn {
|
|
132
|
+
selectedIds: Set<string>;
|
|
133
|
+
setSelectedIds: React.Dispatch<React.SetStateAction<Set<string>>>;
|
|
134
|
+
selectionBox: {
|
|
135
|
+
x: number;
|
|
136
|
+
y: number;
|
|
137
|
+
width: number;
|
|
138
|
+
height: number;
|
|
139
|
+
} | null;
|
|
140
|
+
isSelecting: boolean;
|
|
141
|
+
startSelection: (event: React.MouseEvent) => void;
|
|
142
|
+
updateSelection: (event: React.MouseEvent) => void;
|
|
143
|
+
endSelection: () => void;
|
|
144
|
+
toggleSelect: (nodeId: string, additive?: boolean) => void;
|
|
145
|
+
selectAll: () => void;
|
|
146
|
+
clearSelection: () => void;
|
|
147
|
+
}
|
|
148
|
+
export interface UseNodeDragReturn {
|
|
149
|
+
draggedNodeId: string | null;
|
|
150
|
+
isDragging: boolean;
|
|
151
|
+
dragOffset: {
|
|
152
|
+
x: number;
|
|
153
|
+
y: number;
|
|
154
|
+
};
|
|
155
|
+
startDrag: (nodeId: string, event: React.MouseEvent) => void;
|
|
156
|
+
updateDrag: (event: React.MouseEvent) => void;
|
|
157
|
+
endDrag: () => void;
|
|
158
|
+
}
|