-
This commit is contained in:
2
web-components/.gitignore
vendored
Normal file
2
web-components/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
dist
|
||||
node_modules
|
||||
@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import { TablerIconsProps } from '@tabler/icons-react';
|
||||
export interface ActionMenuItem {
|
||||
key: string;
|
||||
label: string;
|
||||
icon?: React.ComponentType<TablerIconsProps>;
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
onClick: (item?: any) => void | Promise<void>;
|
||||
confirm?: {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
};
|
||||
show?: (item: any) => boolean;
|
||||
}
|
||||
export interface ActionMenuProps {
|
||||
item?: any;
|
||||
actions: ActionMenuItem[];
|
||||
trigger?: 'dots' | 'button' | 'custom';
|
||||
triggerLabel?: string;
|
||||
triggerIcon?: React.ComponentType<TablerIconsProps>;
|
||||
triggerProps?: any;
|
||||
customTrigger?: React.ReactNode;
|
||||
position?: 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start';
|
||||
withArrow?: boolean;
|
||||
withinPortal?: boolean;
|
||||
'aria-label'?: string;
|
||||
}
|
||||
declare const ActionMenu: React.FC<ActionMenuProps>;
|
||||
export default ActionMenu;
|
||||
export declare const createViewAction: (onView: (item: any) => void) => ActionMenuItem;
|
||||
export declare const createEditAction: (onEdit: (item: any) => void) => ActionMenuItem;
|
||||
export declare const createCopyAction: (onCopy: (item: any) => void) => ActionMenuItem;
|
||||
export declare const createDeleteAction: (onDelete: (item: any) => void | Promise<void>, itemName?: string) => ActionMenuItem;
|
||||
export declare const createArchiveAction: (onArchive: (item: any) => void) => ActionMenuItem;
|
||||
export declare const createRestoreAction: (onRestore: (item: any) => void) => ActionMenuItem;
|
||||
export declare const getUserActions: (onEdit: (item: any) => void, onDelete: (item: any) => void, onViewDetails?: (item: any) => void) => ActionMenuItem[];
|
||||
export declare const getApplicationActions: (onEdit: (item: any) => void, onDelete: (item: any) => void, onConfigure?: (item: any) => void) => ActionMenuItem[];
|
||||
export declare const getFunctionActions: (onEdit: (item: any) => void, onDelete: (item: any) => void, onExecute?: (item: any) => void, onViewLogs?: (item: any) => void) => ActionMenuItem[];
|
||||
export declare const getTokenActions: (onRevoke: (item: any) => void, onCopy?: (item: any) => void, onRefresh?: (item: any) => void) => ActionMenuItem[];
|
||||
@ -1,45 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ListItem, FilterOptions } from '../../types';
|
||||
export interface TableColumn {
|
||||
key: string;
|
||||
label: string;
|
||||
sortable?: boolean;
|
||||
filterable?: boolean;
|
||||
width?: string | number;
|
||||
render?: (value: any, item: ListItem) => React.ReactNode;
|
||||
}
|
||||
export interface TableAction {
|
||||
key: string;
|
||||
label: string;
|
||||
icon?: React.ReactNode;
|
||||
color?: string;
|
||||
onClick: (item: ListItem) => void;
|
||||
show?: (item: ListItem) => boolean;
|
||||
}
|
||||
export interface DataTableProps {
|
||||
data: ListItem[];
|
||||
columns: TableColumn[];
|
||||
loading?: boolean;
|
||||
error?: string | null;
|
||||
title?: string;
|
||||
total?: number;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
onPageChange?: (page: number) => void;
|
||||
onAdd?: () => void;
|
||||
onEdit?: (item: ListItem) => void;
|
||||
onDelete?: (item: ListItem) => Promise<void>;
|
||||
onRefresh?: () => void;
|
||||
customActions?: TableAction[];
|
||||
searchable?: boolean;
|
||||
filterable?: boolean;
|
||||
filters?: FilterOptions;
|
||||
onFiltersChange?: (filters: FilterOptions) => void;
|
||||
withBorder?: boolean;
|
||||
withColumnBorders?: boolean;
|
||||
striped?: boolean;
|
||||
highlightOnHover?: boolean;
|
||||
emptyMessage?: string;
|
||||
}
|
||||
declare const DataTable: React.FC<DataTableProps>;
|
||||
export default DataTable;
|
||||
@ -1,47 +0,0 @@
|
||||
import React from 'react';
|
||||
import { TablerIconsProps } from '@tabler/icons-react';
|
||||
export type EmptyStateVariant = 'no-data' | 'no-results' | 'error' | 'loading-failed' | 'access-denied' | 'coming-soon';
|
||||
export type EmptyStateContext = 'users' | 'applications' | 'functions' | 'tokens' | 'executions' | 'permissions' | 'audit' | 'generic';
|
||||
export interface EmptyStateAction {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
variant?: 'filled' | 'light' | 'outline';
|
||||
color?: string;
|
||||
leftSection?: React.ReactNode;
|
||||
}
|
||||
export interface EmptyStateProps {
|
||||
variant?: EmptyStateVariant;
|
||||
context?: EmptyStateContext;
|
||||
title?: string;
|
||||
message?: string;
|
||||
icon?: React.ComponentType<TablerIconsProps>;
|
||||
iconSize?: number;
|
||||
iconColor?: string;
|
||||
actions?: EmptyStateAction[];
|
||||
height?: number | string;
|
||||
}
|
||||
declare const EmptyState: React.FC<EmptyStateProps & {
|
||||
onAdd?: () => void;
|
||||
onRefresh?: () => void;
|
||||
onClearFilters?: () => void;
|
||||
}>;
|
||||
export default EmptyState;
|
||||
export declare const NoUsersState: React.FC<Omit<EmptyStateProps, 'context' | 'variant'> & {
|
||||
onAddUser?: () => void;
|
||||
}>;
|
||||
export declare const NoApplicationsState: React.FC<Omit<EmptyStateProps, 'context' | 'variant'> & {
|
||||
onCreateApp?: () => void;
|
||||
}>;
|
||||
export declare const NoFunctionsState: React.FC<Omit<EmptyStateProps, 'context' | 'variant'> & {
|
||||
onCreateFunction?: () => void;
|
||||
}>;
|
||||
export declare const NoTokensState: React.FC<Omit<EmptyStateProps, 'context' | 'variant'> & {
|
||||
onGenerateToken?: () => void;
|
||||
}>;
|
||||
export declare const NoSearchResults: React.FC<Omit<EmptyStateProps, 'variant'> & {
|
||||
onClearFilters?: () => void;
|
||||
onRefresh?: () => void;
|
||||
}>;
|
||||
export declare const ErrorState: React.FC<Omit<EmptyStateProps, 'variant'> & {
|
||||
onRetry?: () => void;
|
||||
}>;
|
||||
@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FormField } from '../../types';
|
||||
export interface FormSidebarProps {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
title: string;
|
||||
editMode?: boolean;
|
||||
editItem?: any;
|
||||
fields: FormField[];
|
||||
onSubmit: (values: any) => Promise<void>;
|
||||
width?: number;
|
||||
initialValues?: Record<string, any>;
|
||||
validateOnSubmit?: boolean;
|
||||
}
|
||||
declare const FormSidebar: React.FC<FormSidebarProps>;
|
||||
export default FormSidebar;
|
||||
@ -1,46 +0,0 @@
|
||||
import React from 'react';
|
||||
export type LoadingVariant = 'spinner' | 'progress' | 'skeleton-table' | 'skeleton-cards' | 'skeleton-form' | 'skeleton-text' | 'dots' | 'overlay';
|
||||
export type LoadingSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
||||
export interface LoadingStateProps {
|
||||
variant?: LoadingVariant;
|
||||
size?: LoadingSize;
|
||||
height?: number | string;
|
||||
message?: string;
|
||||
submessage?: string;
|
||||
progress?: number;
|
||||
progressLabel?: string;
|
||||
rows?: number;
|
||||
columns?: number;
|
||||
color?: string;
|
||||
withContainer?: boolean;
|
||||
animate?: boolean;
|
||||
}
|
||||
declare const LoadingState: React.FC<LoadingStateProps>;
|
||||
export default LoadingState;
|
||||
export declare const TableLoadingState: React.FC<{
|
||||
rows?: number;
|
||||
columns?: number;
|
||||
}>;
|
||||
export declare const CardsLoadingState: React.FC<{
|
||||
count?: number;
|
||||
columns?: number;
|
||||
}>;
|
||||
export declare const FormLoadingState: React.FC<{
|
||||
fields?: number;
|
||||
}>;
|
||||
export declare const PageLoadingState: React.FC<{
|
||||
message?: string;
|
||||
}>;
|
||||
export declare const InlineLoadingState: React.FC<{
|
||||
message?: string;
|
||||
size?: LoadingSize;
|
||||
}>;
|
||||
export declare const useLoadingState: (initialLoading?: boolean) => {
|
||||
loading: boolean;
|
||||
progress: number;
|
||||
startLoading: () => void;
|
||||
stopLoading: () => void;
|
||||
updateProgress: (value: number) => void;
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setProgress: React.Dispatch<React.SetStateAction<number>>;
|
||||
};
|
||||
@ -1,49 +0,0 @@
|
||||
import React from 'react';
|
||||
export interface SidebarProps {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
width?: number;
|
||||
position?: 'left' | 'right';
|
||||
headerActions?: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
zIndex?: number;
|
||||
offsetTop?: number;
|
||||
backgroundColor?: string;
|
||||
borderColor?: string;
|
||||
animationDuration?: string;
|
||||
'aria-label'?: string;
|
||||
}
|
||||
declare const Sidebar: React.FC<SidebarProps>;
|
||||
export default Sidebar;
|
||||
export interface FormSidebarWrapperProps extends Omit<SidebarProps, 'children'> {
|
||||
children: React.ReactNode;
|
||||
cancelLabel?: string;
|
||||
submitLabel?: string;
|
||||
onCancel?: () => void;
|
||||
onSubmit?: () => void;
|
||||
submitDisabled?: boolean;
|
||||
showFooterActions?: boolean;
|
||||
}
|
||||
export declare const FormSidebarWrapper: React.FC<FormSidebarWrapperProps>;
|
||||
export interface DetailsSidebarProps extends Omit<SidebarProps, 'title'> {
|
||||
itemName: string;
|
||||
itemType?: string;
|
||||
editButton?: React.ReactNode;
|
||||
deleteButton?: React.ReactNode;
|
||||
status?: React.ReactNode;
|
||||
}
|
||||
export declare const DetailsSidebar: React.FC<DetailsSidebarProps>;
|
||||
export interface QuickSidebarProps extends Omit<SidebarProps, 'children'> {
|
||||
content: React.ReactNode;
|
||||
actions?: React.ReactNode;
|
||||
}
|
||||
export declare const QuickSidebar: React.FC<QuickSidebarProps>;
|
||||
export declare const useSidebar: (initialOpened?: boolean) => {
|
||||
opened: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
toggle: () => void;
|
||||
setOpened: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { BadgeProps } from '@mantine/core';
|
||||
export type StatusVariant = 'status' | 'role' | 'runtime' | 'type' | 'severity' | 'execution';
|
||||
export interface StatusBadgeProps extends Omit<BadgeProps, 'color' | 'children'> {
|
||||
value: string;
|
||||
variant?: StatusVariant;
|
||||
customColorMap?: Record<string, string>;
|
||||
}
|
||||
declare const COLOR_MAPS: Record<StatusVariant, Record<string, string>>;
|
||||
declare const DEFAULT_COLORS: Record<StatusVariant, string>;
|
||||
declare const StatusBadge: React.FC<StatusBadgeProps>;
|
||||
export default StatusBadge;
|
||||
export { COLOR_MAPS, DEFAULT_COLORS };
|
||||
export declare const UserRoleBadge: React.FC<Omit<StatusBadgeProps, 'variant'>>;
|
||||
export declare const ApplicationTypeBadge: React.FC<Omit<StatusBadgeProps, 'variant'>>;
|
||||
export declare const RuntimeBadge: React.FC<Omit<StatusBadgeProps, 'variant'>>;
|
||||
export declare const ExecutionStatusBadge: React.FC<Omit<StatusBadgeProps, 'variant'>>;
|
||||
export declare const SeverityBadge: React.FC<Omit<StatusBadgeProps, 'variant'>>;
|
||||
25
web-components/dist/hooks/useApiService.d.ts
vendored
25
web-components/dist/hooks/useApiService.d.ts
vendored
@ -1,25 +0,0 @@
|
||||
import { AxiosInstance } from 'axios';
|
||||
import { FilterOptions } from '../types';
|
||||
export interface ApiServiceConfig {
|
||||
baseURL: string;
|
||||
defaultHeaders?: Record<string, string>;
|
||||
timeout?: number;
|
||||
}
|
||||
export interface UseApiServiceReturn<T> {
|
||||
data: T[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
client: AxiosInstance;
|
||||
getAll: (filters?: FilterOptions) => Promise<T[]>;
|
||||
getById: (id: string) => Promise<T>;
|
||||
create: (data: Partial<T>) => Promise<T>;
|
||||
update: (id: string, data: Partial<T>) => Promise<T>;
|
||||
delete: (id: string) => Promise<void>;
|
||||
clearError: () => void;
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
export declare const useApiService: <T extends {
|
||||
id: string;
|
||||
}>(config: ApiServiceConfig, endpoint: string) => UseApiServiceReturn<T>;
|
||||
16
web-components/dist/hooks/useDataFilter.d.ts
vendored
16
web-components/dist/hooks/useDataFilter.d.ts
vendored
@ -1,16 +0,0 @@
|
||||
import { FilterOptions, ListItem } from '../types';
|
||||
export interface UseDataFilterOptions {
|
||||
searchFields?: string[];
|
||||
defaultFilters?: FilterOptions;
|
||||
debounceMs?: number;
|
||||
}
|
||||
export interface UseDataFilterReturn {
|
||||
filteredData: ListItem[];
|
||||
filters: FilterOptions;
|
||||
setFilter: (key: string, value: any) => void;
|
||||
clearFilters: () => void;
|
||||
resetFilters: () => void;
|
||||
searchTerm: string;
|
||||
setSearchTerm: (term: string) => void;
|
||||
}
|
||||
export declare const useDataFilter: (data: ListItem[], options?: UseDataFilterOptions) => UseDataFilterReturn;
|
||||
18
web-components/dist/index.d.ts
vendored
18
web-components/dist/index.d.ts
vendored
@ -1,18 +0,0 @@
|
||||
export { default as FormSidebar } from './components/FormSidebar/FormSidebar';
|
||||
export { default as DataTable } from './components/DataTable/DataTable';
|
||||
export { default as StatusBadge, UserRoleBadge, ApplicationTypeBadge, RuntimeBadge, ExecutionStatusBadge, SeverityBadge } from './components/StatusBadge/StatusBadge';
|
||||
export { default as EmptyState, NoUsersState, NoApplicationsState, NoFunctionsState, NoTokensState, NoSearchResults, ErrorState } from './components/EmptyState/EmptyState';
|
||||
export { default as Sidebar, FormSidebarWrapper, DetailsSidebar, QuickSidebar, useSidebar } from './components/Sidebar/Sidebar';
|
||||
export { default as ActionMenu, createViewAction, createEditAction, createCopyAction, createDeleteAction, createArchiveAction, createRestoreAction, getUserActions, getApplicationActions, getFunctionActions, getTokenActions } from './components/ActionMenu/ActionMenu';
|
||||
export { default as LoadingState, TableLoadingState, CardsLoadingState, FormLoadingState, PageLoadingState, InlineLoadingState, useLoadingState } from './components/LoadingState/LoadingState';
|
||||
export * from './types';
|
||||
export { useApiService } from './hooks/useApiService';
|
||||
export { useDataFilter } from './hooks/useDataFilter';
|
||||
export * from './utils/notifications';
|
||||
export * from './utils/validation';
|
||||
export { Paper, Stack, Group, Button, TextInput, Select, MultiSelect, NumberInput, Textarea, JsonInput, ActionIcon, Menu, Text, Title, Badge, Table, Pagination, LoadingOverlay, Center, Box, ScrollArea, Divider, } from '@mantine/core';
|
||||
export { useDisclosure, useToggle, useLocalStorage, } from '@mantine/hooks';
|
||||
export { useForm } from '@mantine/form';
|
||||
export { notifications } from '@mantine/notifications';
|
||||
export { modals } from '@mantine/modals';
|
||||
export { IconPlus, IconEdit, IconTrash, IconSearch, IconFilter, IconRefresh, IconX, IconDots, IconChevronDown, IconChevronRight, IconUser, IconUsers, IconKey, IconSettings, IconEye, IconEyeOff, IconCopy, IconCheck, IconAlertCircle, IconInfoCircle, } from '@tabler/icons-react';
|
||||
2
web-components/dist/index.esm.js
vendored
2
web-components/dist/index.esm.js
vendored
File diff suppressed because one or more lines are too long
1
web-components/dist/index.esm.js.map
vendored
1
web-components/dist/index.esm.js.map
vendored
File diff suppressed because one or more lines are too long
2
web-components/dist/index.js
vendored
2
web-components/dist/index.js
vendored
File diff suppressed because one or more lines are too long
1
web-components/dist/index.js.map
vendored
1
web-components/dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
84
web-components/dist/types/index.d.ts
vendored
84
web-components/dist/types/index.d.ts
vendored
@ -1,84 +0,0 @@
|
||||
export interface BaseEntity {
|
||||
id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by?: string;
|
||||
updated_by?: string;
|
||||
}
|
||||
export interface Owner {
|
||||
type: 'individual' | 'team';
|
||||
name: string;
|
||||
owner: string;
|
||||
}
|
||||
export interface FormSidebarProps {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
editItem?: any;
|
||||
}
|
||||
export interface ListItem {
|
||||
id: string;
|
||||
name?: string;
|
||||
title?: string;
|
||||
email?: string;
|
||||
status?: string;
|
||||
role?: string;
|
||||
type?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface ApiResponse<T> {
|
||||
data: T;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
has_more: boolean;
|
||||
}
|
||||
export interface FilterOptions {
|
||||
search?: string;
|
||||
status?: string;
|
||||
type?: string;
|
||||
role?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface NotificationConfig {
|
||||
title: string;
|
||||
message: string;
|
||||
color: 'red' | 'green' | 'blue' | 'yellow' | 'gray';
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface ValidationRule {
|
||||
required?: boolean;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
pattern?: RegExp;
|
||||
email?: boolean;
|
||||
url?: boolean;
|
||||
custom?: (value: any) => string | null;
|
||||
}
|
||||
export interface FormField {
|
||||
name: string;
|
||||
label: string;
|
||||
type: 'text' | 'email' | 'number' | 'select' | 'multiselect' | 'textarea' | 'date' | 'json';
|
||||
placeholder?: string;
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
options?: Array<{
|
||||
value: string;
|
||||
label: string;
|
||||
}>;
|
||||
validation?: ValidationRule;
|
||||
defaultValue?: any;
|
||||
}
|
||||
export type { StatusVariant, StatusBadgeProps } from '../components/StatusBadge/StatusBadge';
|
||||
export type { EmptyStateVariant, EmptyStateContext, EmptyStateProps, EmptyStateAction } from '../components/EmptyState/EmptyState';
|
||||
export type { SidebarProps, FormSidebarWrapperProps, DetailsSidebarProps, QuickSidebarProps } from '../components/Sidebar/Sidebar';
|
||||
export type { ActionMenuItem, ActionMenuProps } from '../components/ActionMenu/ActionMenu';
|
||||
export type { LoadingVariant, LoadingSize, LoadingStateProps } from '../components/LoadingState/LoadingState';
|
||||
37
web-components/dist/utils/notifications.d.ts
vendored
37
web-components/dist/utils/notifications.d.ts
vendored
@ -1,37 +0,0 @@
|
||||
export declare const showSuccessNotification: (message: string, title?: string) => void;
|
||||
export declare const showErrorNotification: (message: string, title?: string) => void;
|
||||
export declare const showWarningNotification: (message: string, title?: string) => void;
|
||||
export declare const showInfoNotification: (message: string, title?: string) => void;
|
||||
export declare const NotificationMessages: {
|
||||
createSuccess: (entityName: string) => string;
|
||||
updateSuccess: (entityName: string) => string;
|
||||
deleteSuccess: (entityName: string) => string;
|
||||
createError: (entityName: string) => string;
|
||||
updateError: (entityName: string) => string;
|
||||
deleteError: (entityName: string) => string;
|
||||
loadError: (entityName: string) => string;
|
||||
networkError: string;
|
||||
validationError: string;
|
||||
requiredFieldError: (fieldName: string) => string;
|
||||
authRequired: string;
|
||||
permissionDenied: string;
|
||||
sessionExpired: string;
|
||||
applicationCreated: string;
|
||||
applicationUpdated: string;
|
||||
applicationDeleted: string;
|
||||
tokenCreated: string;
|
||||
tokenRevoked: string;
|
||||
userCreated: string;
|
||||
userUpdated: string;
|
||||
userDeleted: string;
|
||||
functionCreated: string;
|
||||
functionUpdated: string;
|
||||
functionDeleted: string;
|
||||
executionStarted: string;
|
||||
executionCompleted: string;
|
||||
executionFailed: string;
|
||||
};
|
||||
export declare const showCrudNotification: {
|
||||
success: (operation: "create" | "update" | "delete", entityName: string) => void;
|
||||
error: (operation: "create" | "update" | "delete" | "load", entityName: string, customMessage?: string) => void;
|
||||
};
|
||||
37
web-components/dist/utils/validation.d.ts
vendored
37
web-components/dist/utils/validation.d.ts
vendored
@ -1,37 +0,0 @@
|
||||
export declare const ValidationPatterns: {
|
||||
email: RegExp;
|
||||
url: RegExp;
|
||||
duration: RegExp;
|
||||
token: RegExp;
|
||||
appId: RegExp;
|
||||
uuid: RegExp;
|
||||
};
|
||||
export declare const ValidationMessages: {
|
||||
required: (fieldName: string) => string;
|
||||
email: string;
|
||||
url: string;
|
||||
duration: string;
|
||||
minLength: (fieldName: string, minLength: number) => string;
|
||||
maxLength: (fieldName: string, maxLength: number) => string;
|
||||
pattern: (fieldName: string) => string;
|
||||
token: string;
|
||||
appId: string;
|
||||
uuid: string;
|
||||
positiveNumber: string;
|
||||
range: (fieldName: string, min: number, max: number) => string;
|
||||
};
|
||||
export declare const validateRequired: (value: any) => string | null;
|
||||
export declare const validateEmail: (value: string) => string | null;
|
||||
export declare const validateUrl: (value: string) => string | null;
|
||||
export declare const validateDuration: (value: string) => string | null;
|
||||
export declare const validateMinLength: (value: string, minLength: number, fieldName?: string) => string | null;
|
||||
export declare const validateMaxLength: (value: string, maxLength: number, fieldName?: string) => string | null;
|
||||
export declare const validatePattern: (value: string, pattern: RegExp, fieldName?: string) => string | null;
|
||||
export declare const validateRange: (value: number, min: number, max: number, fieldName?: string) => string | null;
|
||||
export declare const validateAppId: (value: string) => string | null;
|
||||
export declare const validateToken: (value: string) => string | null;
|
||||
export declare const validateUuid: (value: string) => string | null;
|
||||
export declare const validateJsonString: (value: string) => string | null;
|
||||
export declare const parseDuration: (duration: string) => number;
|
||||
export declare const formatDuration: (seconds: number) => string;
|
||||
export declare const combineValidators: (...validators: Array<(value: any) => string | null>) => (value: any) => string | null;
|
||||
@ -20,6 +20,9 @@ export interface SidebarProps {
|
||||
footer?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
|
||||
// Layout mode - when true, sidebar fills container instead of fixed positioning
|
||||
layoutMode?: boolean;
|
||||
|
||||
// Styling customization
|
||||
zIndex?: number;
|
||||
offsetTop?: number;
|
||||
@ -42,6 +45,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
headerActions,
|
||||
footer,
|
||||
children,
|
||||
layoutMode = false,
|
||||
zIndex = 1000,
|
||||
offsetTop = 60,
|
||||
backgroundColor = 'var(--mantine-color-body)',
|
||||
@ -49,30 +53,46 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
animationDuration = '0.3s',
|
||||
'aria-label': ariaLabel,
|
||||
}) => {
|
||||
// Calculate position styles based on position prop
|
||||
// Calculate position styles based on layout mode
|
||||
const getPositionStyles = () => {
|
||||
const baseStyles = {
|
||||
position: 'fixed' as const,
|
||||
top: offsetTop,
|
||||
bottom: 0,
|
||||
width: `${width}px`,
|
||||
zIndex,
|
||||
borderRadius: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as const,
|
||||
backgroundColor,
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
if (layoutMode) {
|
||||
// In layout mode, sidebar fills its container (managed by SidebarLayout)
|
||||
return {
|
||||
...baseStyles,
|
||||
position: 'relative' as const,
|
||||
borderLeft: position === 'right' ? `1px solid ${borderColor}` : undefined,
|
||||
borderRight: position === 'left' ? `1px solid ${borderColor}` : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// Legacy fixed positioning mode (for backward compatibility)
|
||||
const fixedStyles = {
|
||||
...baseStyles,
|
||||
position: 'fixed' as const,
|
||||
top: offsetTop,
|
||||
bottom: 0,
|
||||
zIndex,
|
||||
transition: `${position} ${animationDuration} ease`,
|
||||
};
|
||||
|
||||
if (position === 'right') {
|
||||
return {
|
||||
...baseStyles,
|
||||
...fixedStyles,
|
||||
right: opened ? 0 : `-${width}px`,
|
||||
borderLeft: `1px solid ${borderColor}`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...baseStyles,
|
||||
...fixedStyles,
|
||||
left: opened ? 0 : `-${width}px`,
|
||||
borderRight: `1px solid ${borderColor}`,
|
||||
};
|
||||
|
||||
170
web-components/src/components/SidebarLayout/SidebarLayout.tsx
Normal file
170
web-components/src/components/SidebarLayout/SidebarLayout.tsx
Normal file
@ -0,0 +1,170 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@mantine/core';
|
||||
|
||||
export interface SidebarLayoutProps {
|
||||
children: React.ReactNode;
|
||||
sidebar?: React.ReactNode;
|
||||
sidebarOpened?: boolean;
|
||||
sidebarWidth?: number;
|
||||
sidebarPosition?: 'left' | 'right';
|
||||
offsetTop?: number;
|
||||
className?: string;
|
||||
|
||||
// Animation settings
|
||||
transitionDuration?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SidebarLayout provides a responsive layout that shrinks the main content area
|
||||
* when a sidebar is opened, rather than overlaying on top of the content.
|
||||
*
|
||||
* This ensures the main content remains visible and accessible when sidebars are open.
|
||||
*/
|
||||
const SidebarLayout: React.FC<SidebarLayoutProps> = ({
|
||||
children,
|
||||
sidebar,
|
||||
sidebarOpened = false,
|
||||
sidebarWidth = 450,
|
||||
sidebarPosition = 'right',
|
||||
offsetTop = 60,
|
||||
transitionDuration = '0.3s',
|
||||
className,
|
||||
}) => {
|
||||
// Calculate main content area margins based on sidebar state
|
||||
const getMainContentStyles = (): React.CSSProperties => {
|
||||
if (!sidebarOpened) {
|
||||
return {
|
||||
marginLeft: 0,
|
||||
marginRight: 0,
|
||||
transition: `margin ${transitionDuration} ease`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
marginLeft: sidebarPosition === 'left' ? `${sidebarWidth}px` : 0,
|
||||
marginRight: sidebarPosition === 'right' ? `${sidebarWidth}px` : 0,
|
||||
transition: `margin ${transitionDuration} ease`,
|
||||
};
|
||||
};
|
||||
|
||||
// Calculate sidebar container styles for proper positioning
|
||||
const getSidebarContainerStyles = (): React.CSSProperties => ({
|
||||
position: 'fixed',
|
||||
top: offsetTop,
|
||||
bottom: 0,
|
||||
width: `${sidebarWidth}px`,
|
||||
zIndex: 1000,
|
||||
[sidebarPosition]: sidebarOpened ? 0 : `-${sidebarWidth}px`,
|
||||
transition: `${sidebarPosition} ${transitionDuration} ease`,
|
||||
pointerEvents: sidebarOpened ? 'auto' : 'none',
|
||||
});
|
||||
|
||||
return (
|
||||
<Box className={className} style={{ position: 'relative', minHeight: '100%' }}>
|
||||
{/* Main Content Area - adjusts width based on sidebar state */}
|
||||
<Box style={getMainContentStyles()}>
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
{/* Sidebar Container - positioned absolutely but doesn't overlay content */}
|
||||
{sidebar && (
|
||||
<Box style={getSidebarContainerStyles()}>
|
||||
{sidebar}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarLayout;
|
||||
|
||||
/**
|
||||
* Higher-level wrapper that combines SidebarLayout with responsive behavior
|
||||
* and mobile-friendly overlays when screen size is too small.
|
||||
*/
|
||||
export interface ResponsiveSidebarLayoutProps extends SidebarLayoutProps {
|
||||
mobileBreakpoint?: number;
|
||||
overlayOnMobile?: boolean;
|
||||
}
|
||||
|
||||
export const ResponsiveSidebarLayout: React.FC<ResponsiveSidebarLayoutProps> = ({
|
||||
mobileBreakpoint = 768,
|
||||
overlayOnMobile = true,
|
||||
...props
|
||||
}) => {
|
||||
const [isMobile, setIsMobile] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const checkMobile = () => {
|
||||
setIsMobile(window.innerWidth < mobileBreakpoint);
|
||||
};
|
||||
|
||||
checkMobile();
|
||||
window.addEventListener('resize', checkMobile);
|
||||
return () => window.removeEventListener('resize', checkMobile);
|
||||
}, [mobileBreakpoint]);
|
||||
|
||||
// On mobile, use overlay behavior instead of shrinking content
|
||||
if (isMobile && overlayOnMobile) {
|
||||
return (
|
||||
<Box style={{ position: 'relative', minHeight: '100%' }}>
|
||||
{props.children}
|
||||
{props.sidebar && props.sidebarOpened && (
|
||||
<Box
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: props.offsetTop || 60,
|
||||
bottom: 0,
|
||||
[props.sidebarPosition || 'right']: 0,
|
||||
width: `${props.sidebarWidth || 450}px`,
|
||||
zIndex: 1000,
|
||||
transition: `transform ${props.transitionDuration || '0.3s'} ease`,
|
||||
}}
|
||||
>
|
||||
{props.sidebar}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Mobile overlay backdrop */}
|
||||
{props.sidebarOpened && (
|
||||
<Box
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
zIndex: 999,
|
||||
}}
|
||||
onClick={() => {
|
||||
// If sidebar has onClose, call it
|
||||
if (React.isValidElement(props.sidebar) && props.sidebar.props.onClose) {
|
||||
props.sidebar.props.onClose();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return <SidebarLayout {...props} />;
|
||||
};
|
||||
|
||||
// Hook for managing sidebar layout state
|
||||
export const useSidebarLayout = (initialOpened = false) => {
|
||||
const [sidebarOpened, setSidebarOpened] = React.useState(initialOpened);
|
||||
|
||||
const openSidebar = React.useCallback(() => setSidebarOpened(true), []);
|
||||
const closeSidebar = React.useCallback(() => setSidebarOpened(false), []);
|
||||
const toggleSidebar = React.useCallback(() => setSidebarOpened(prev => !prev), []);
|
||||
|
||||
return {
|
||||
sidebarOpened,
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
toggleSidebar,
|
||||
setSidebarOpened,
|
||||
};
|
||||
};
|
||||
@ -4,6 +4,7 @@ export { default as DataTable } from './components/DataTable/DataTable';
|
||||
export { default as StatusBadge, UserRoleBadge, ApplicationTypeBadge, RuntimeBadge, ExecutionStatusBadge, SeverityBadge } from './components/StatusBadge/StatusBadge';
|
||||
export { default as EmptyState, NoUsersState, NoApplicationsState, NoFunctionsState, NoTokensState, NoSearchResults, ErrorState } from './components/EmptyState/EmptyState';
|
||||
export { default as Sidebar, FormSidebarWrapper, DetailsSidebar, QuickSidebar, useSidebar } from './components/Sidebar/Sidebar';
|
||||
export { default as SidebarLayout, ResponsiveSidebarLayout, useSidebarLayout } from './components/SidebarLayout/SidebarLayout';
|
||||
export { default as ActionMenu, createViewAction, createEditAction, createCopyAction, createDeleteAction, createArchiveAction, createRestoreAction, getUserActions, getApplicationActions, getFunctionActions, getTokenActions } from './components/ActionMenu/ActionMenu';
|
||||
export { default as LoadingState, TableLoadingState, CardsLoadingState, FormLoadingState, PageLoadingState, InlineLoadingState, useLoadingState } from './components/LoadingState/LoadingState';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user