1.0.47 • Published 6 months ago

@datavysta/vysta-react v1.0.47

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

@datavysta/vysta-react

React components for Vysta - a backend as a service platform.

Installation

npm install @datavysta/vysta-react @datavysta/vysta-client ag-grid-community ag-grid-react

Basic Usage

First, import the styles:

import '@datavysta/vysta-react/style.css';

Then use the components in your app:

import { DataGrid } from '@datavysta/vysta-react';
import { VystaClient, VystaService } from '@datavysta/vysta-client';
import { useMemo } from 'react';

// Define your entity type
interface Product {
  productId: number;
  productName: string;
  unitPrice: number;
}

// Create your service
class ProductService extends VystaService<Product> {
  constructor(client: VystaClient) {
    super(client, 'Northwinds', 'Products', {
      primaryKey: 'productId'
    });
  }
}

// In your React component
function ProductList() {
  const products = useMemo(() => {
    const client = new VystaClient({ baseUrl: 'http://localhost:8080' });
    return new ProductService(client);
  }, []);

  const columnDefs = [
    { field: 'productId', headerName: 'ID' },
    { field: 'productName', headerName: 'Name' },
    { field: 'unitPrice', headerName: 'Price' }
  ];

  return (
    <DataGrid<Product>
      title="Products"
      noun="Product"
      repository={products}
      columnDefs={columnDefs}
      getRowId={(product) => product.productId.toString()}
    />
  );
}

## VystaServiceProvider (Core Service Context)

`VystaServiceProvider` is a core feature that provides the VystaClient instance and core Vysta services (roles, permissions, user profile, and authentication) to your app via React context.

**Usage Example:**

```tsx
import { VystaServiceProvider, useVystaServices } from '@datavysta/vysta-react';
import { VystaConfig } from '@datavysta/vysta-client';

const config: VystaConfig = {
  baseUrl: '/api',
  debug: true,
};

function App() {
  return (
    <VystaServiceProvider config={config} apps={["Northwinds"]}>
      <YourApp />
    </VystaServiceProvider>
  );
}

// In any child component:
function MyComponent() {
  const {
    roleService,
    permissionService,
    profile,
    permissions,
    canSelectConnection,
    isAuthenticated,
    profileLoading,
    profileError,
    loginLoading,
    loginError,
    auth,
  } = useVystaServices();

  // Example: login
  const handleLogin = async () => {
    await auth.login('user@example.com', 'password');
  };

  // Example: logout
  const handleLogout = async () => {
    await auth.logout();
  };

  // Example: get sign-in methods
  const signInMethods = await auth.getSignInMethods();

  // ...use any of the above context values as needed
}

Note: VystaServiceProvider provides the following via context (using useVystaServices):

  • roleService: VystaRoleService instance
  • permissionService: VystaPermissionService instance
  • profile: The user's profile object (or null if not loaded)
  • permissions: A record mapping app/connection names to their permissions (or null if not loaded or not requested)
  • canSelectConnection: Helper to check if the user has SELECT permission for a given app
  • isAuthenticated: Boolean, true if a user profile is loaded
  • profileLoading: Boolean, true while profile/permissions are loading
  • profileError: Any error encountered during profile/permissions loading
  • loginLoading: Boolean, true while a login/logout is in progress
  • loginError: Any error encountered during login/logout
  • auth: An object with authentication methods:
    • login(username, password)
    • logout()
    • getSignInMethods()
    • getAuthorizeUrl(providerId)
    • exchangeToken(token)

Always use the context-based approach for authentication, user, and permissions state. Direct use of internal hooks is not supported in the public API.

Authentication and User Profile (Context-based Access)

Authentication, user profile, and permissions are now accessed exclusively via the VystaServiceProvider and the useVystaServices context hook. This ensures a single source of truth for authentication and user state throughout your app.

  • profile: The user's profile object (or null if not loaded).
  • permissions: A record mapping app/connection names to their permissions (or null if not loaded or not requested).
  • canSelectConnection: Helper to check if the user has SELECT permission for a given app.
  • profileLoading: Boolean, true while profile/permissions are loading.
  • profileError: Any error encountered during profile/permissions loading.
  • loginLoading: Boolean, true while a login/logout is in progress.
  • loginError: Any error encountered during login/logout.
  • isAuthenticated: Boolean, true if a user profile is loaded.
  • auth: An object with authentication methods:
    • login(username, password)
    • logout()
    • getSignInMethods()
    • getAuthorizeUrl(providerId)
    • exchangeToken(token)
  • roleService: VystaRoleService instance for role management.
  • permissionService: VystaPermissionService instance for permission management.

Note: Direct use of internal hooks is not supported in the public API. Always use the context-based approach for authentication and user state.

FilterPanel Component

The FilterPanel component provides a powerful and flexible filtering interface for your data.

Basic Usage

import { FilterPanel } from '@datavysta/vysta-react';
import DataType from '@datavysta/vysta-react/components/Models/DataType';
import { FilterDefinitionsByField } from '@datavysta/vysta-react/components/Filter/FilterDefinitionsByField';

const filterDefinitions: FilterDefinitionsByField = [
    {
        targetFieldName: "productName",
        label: "Product Name",
        dataType: DataType.String
    },
    {
        targetFieldName: "unitPrice",
        label: "Unit Price",
        dataType: DataType.Numeric
    },
    {
        targetFieldName: "unitsInStock",
        label: "Units In Stock",
        dataType: DataType.Numeric
    },
    {
        targetFieldName: "discontinued",
        label: "Discontinued",
        dataType: DataType.Boolean
    }
];

function App() {
    const [conditions, setConditions] = useState<Condition[]>([]);

    return (
        <FilterPanel 
            conditions={conditions}
            onApply={setConditions}
            filterDefinitions={filterDefinitions}
        />
    );
}

FilterPanel Props

PropTypeDescription
filterDefinitionsFilterDefinitionsByFieldArray of field definitions with types and labels
conditionsCondition[]Current filter conditions
onApply(conditions: Condition[]) => voidCallback when filters are applied
onChange(conditions: Condition[]) => voidCallback when conditions change

Filter Types

The FilterPanel supports these data types from DataType:

  • String: Text fields with operations like contains, equals, starts with
  • Numeric: Numeric fields with operations like equals, greater than, less than
  • Boolean: True/false fields
  • Date: Date fields with operations like equals, before, after

Filter Definition Format

Each filter definition should include:

  • targetFieldName: The field name to filter on
  • label: Display label for the field
  • dataType: One of the DataType enum values

Vysta Mantine Integration

Vysta components can optionally use Mantine UI for enhanced styling and interactions. To use Vysta's Mantine integration:

  1. Install Mantine dependencies:
npm install @mantine/core @mantine/hooks
  1. Import Mantine styles:
import '@mantine/core/styles.css';
  1. Wrap your Vysta components with both providers:
import { MantineProvider } from '@mantine/core';
import { VystaMantineComponentProvider } from '@datavysta/vysta-react/mantine';

function App() {
  return (
    <MantineProvider>
      <VystaMantineComponentProvider>
        {/* Your Vysta components here */}
      </VystaMantineComponentProvider>
    </MantineProvider>
  );
}

This will enable Mantine-styled versions of Vysta components while maintaining all their core functionality.

Props Documentation

Required Props

PropTypeDescription
titlestringThe title displayed at the top of the grid
nounstringThe singular noun for your entity (used in button labels)
repositoryIDataService<T>The Vysta service instance for your entity
columnDefsColDef<T>[]AG Grid column definitions
getRowId(data: T) => stringFunction to get a unique string ID from your entity

Optional Props

PropTypeDefaultDescription
gridOptionsGridOptions<T>undefinedAdditional AG Grid options
supportRegularDownloadbooleanfalseEnable CSV download of grid data with current sorting and filtering
supportInsertbooleanfalseShow "New" button
supportDeletebooleanfalseShow delete button in each row
deleteButton(onDelete: () => void) => React.ReactNodeundefinedCustom delete button renderer
filters{ [K in keyof T]?: any }undefinedVysta filters to apply. Should be memoized to prevent unnecessary reloads
wildcardSearchstringundefinedText to use for wildcard search across fields (passed as q parameter)
inputProperties{ [key: string]: any }undefinedAdditional properties to pass to data source. Should be memoized to prevent unnecessary reloads
toolbarItemsReact.ReactNodeundefinedCustom toolbar items
onDataFirstLoaded(gridApi: GridApi<T>) => voidundefinedCallback when data first loads or when filters/inputProperties change
onDataLoaded(gridApi: GridApi<T>, data: T[]) => voidundefinedCallback when any data loads, including incremental loads
getRowClass(params: RowClassParams<T>) => string \| string[] \| undefinedundefinedCustom row CSS classes
ticknumber0Trigger grid refresh when incremented
themeTheme \| 'legacy'undefinedAG Grid theme configuration
stylesDataGridStyles{}Custom styles for grid elements
noRowsComponentReact.ComponentType<any>undefinedCustom component to display when no rows are found
loadingComponentReact.ComponentType<any>undefinedCustom component to display during loading

Styling

You can customize the appearance of individual grid elements using the styles prop:

<DataGrid<Product>
  // ... other props ...
  styles={{
    container: { backgroundColor: '#f5f5f5' },
    title: { color: 'blue', fontSize: '1.2rem' },
    badge: { backgroundColor: 'red', color: 'white' },
    createButton: { backgroundColor: 'green' },
    // ... other style overrides
  }}
/>

Available style targets:

  • container: The main grid container
  • toolbar: The top toolbar section
  • titleSection: The title and badge container
  • title: The grid title text
  • badge: The count badge
  • actions: The actions container (buttons)
  • createButton: The "New" button
  • downloadButton: The download button
  • deleteButton: The delete button in rows
  • grid: The AG Grid container

Features

  • 🔄 Infinite scrolling with server-side operations
  • 📊 Sorting and filtering
  • ✨ Customizable columns
  • 🎨 Modern UI with great UX
  • 💪 Fully typed with TypeScript

Grid Customization

The grid uses AG Grid Community Edition under the hood. You can customize the grid behavior using the gridOptions prop:

<DataGrid<Product>
  // ... required props ...
  gridOptions={{
    rowHeight: 48,
    headerHeight: 40,
    rowSelection: 'multiple',
    // ... any other AG Grid options
  }}
/>

Filtering Example

// Example with memoized filters to prevent unnecessary reloads
function ProductList() {
  const [useFilter, setUseFilter] = useState(false);
  
  const filters = useMemo(
    () => useFilter ? { unitPrice: { gt: 20 } } : undefined,
    [useFilter]
  );

  return (
    <DataGrid<Product>
      // ... other props ...
      filters={filters}
    />
  );
}

Input Properties Example

// Example with memoized input properties
function ProductList() {
  const [useInputProps, setUseInputProps] = useState(false);
  
  const inputProperties = useMemo(
    () => useInputProps ? { customParam: 'value' } : undefined,
    [useInputProps]
  );

  return (
    <DataGrid<Product>
      // ... other props ...
      inputProperties={inputProperties}
    />
  );
}

Aggregate Summary Row (Footer)

DataGrid supports showing a summary/footer row with aggregate values (SUM, AVG, etc) using the Vysta aggregate query API. This is useful for totals, averages, and other summary statistics.

Usage Example

import { DataGrid } from '@datavysta/vysta-react';
import { Aggregate, SelectColumn } from '@datavysta/vysta-client';

const aggregateSelect: SelectColumn<Product>[] = [
  { name: 'unitPrice', aggregate: Aggregate.AVG, alias: 'avgUnitPrice' },
  { name: 'unitsInStock', aggregate: Aggregate.SUM, alias: 'totalUnitsInStock' },
];

<DataGrid<Product>
  title="Products"
  noun="Product"
  repository={productService}
  columnDefs={columnDefs}
  getRowId={(product) => product.productId.toString()}
  aggregateSelect={aggregateSelect}
/>
  • The summary row appears below the grid and updates with filters, search, etc.
  • By default, the row matches columns by field name or alias. You can customize rendering with the renderAggregateFooter prop.
  • The summary row is not part of the grid (not a pinned row), so it is always visible below the grid.

Details & Customization

  • Column value formatting: The footer automatically applies the column's valueFormatter (if provided in your columnDefs). This lets you format numbers, currency, dates, etc., exactly the same way as in the grid cells.
  • Alias required: Every SelectColumn in aggregateSelect must have an alias. The alias is used to look up the aggregate value in the server response.
  • Column matching: The footer places the aggregate under the column whose field equals the name in the SelectColumn. The alias can be anything you like.
  • Fully custom footer: Provide a renderAggregateFooter prop to supply your own React node. You receive the raw summary object (keyed by alias) and can render any layout, charts, or styled components you need.

Props

PropTypeDescription
aggregateSelectSelectColumn<T>[]Columns/aggregates to fetch and show in the summary row
renderAggregateFooter(summary: Record<string, any>) => React.ReactNodeOptional custom render for the summary row
styles.aggregateFooterReact.CSSPropertiesCustom style for the summary/footer row
styles.aggregateValueReact.CSSPropertiesStyle override for the <span> that holds each formatted value

See the @datavysta/vysta-client docs for more on Aggregate and SelectColumn.

LazyLoadList Component

The LazyLoadList component provides a searchable, lazy-loading dropdown list that efficiently loads data from a Vysta service.

Basic Usage

import { LazyLoadList } from '@datavysta/vysta-react';

function ProductSelector() {
    const [selectedId, setSelectedId] = useState<string | null>(null);
    const productService = useMemo(() => new ProductService(client), [client]);

    return (
        <LazyLoadList<Product>
            repository={productService}
            value={selectedId}
            onChange={setSelectedId}
            label="Select Product"
            displayColumn="productName"
            clearable
        />
    );
}

LazyLoadList Props

PropTypeDescription
repositoryIReadonlyDataService<T>The Vysta service to fetch data from
valuestring \| nullThe currently selected value
onChange(value: string \| null) => voidCallback when selection changes
displayColumnkeyof TThe field to display in the list
labelstringOptional label for the list
filters{ [K in keyof T]?: any }Optional filters to apply
groupBykeyof TOptional field to group items by
pageSizenumberNumber of items to load per page (default: 20)
searchablebooleanWhether to show search input (default: true)
clearablebooleanWhether to show clear button (default: true)
disableInitialValueLoadbooleanDisable initial value query when display matches key (default: false)

Features

  • 🔄 Lazy loading with infinite scroll
  • 🔍 Built-in search functionality
  • 📑 Optional grouping of items
  • 🎯 Efficient loading of selected values
  • 🎨 Customizable styles
  • 💪 Full TypeScript support

Example with Filtering

function OrderSelector() {
    const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);
    const [customerId, setCustomerId] = useState<string | null>(null);
    
    return (
        <LazyLoadList<Order>
            repository={orderService}
            value={selectedOrderId}
            onChange={setSelectedOrderId}
            label="Select Order"
            displayColumn="orderId"
            clearable={true}
            filters={customerId ? { customerId: { eq: customerId } } : undefined}
        />
    );
}

FileUpload Component

A file upload component that integrates with Vysta's file service and uses Uppy for the upload interface.

import { FileUpload } from '@datavysta/vysta-react';

function MyComponent() {
    return (
        <FileUpload
            fileService={fileService}
            allowedFileTypes={['.jpg', '.png', 'image/*']}
            filename="custom-name.jpg"
            autoProceed={true}
            onUploadSuccess={(fileId, fileName) => {
                console.log(`File uploaded: ${fileName} with ID: ${fileId}`);
            }}
        />
    );
}

Props

PropTypeDefaultDescription
fileServiceVystaFileServiceRequiredThe Vysta file service instance to handle uploads
onUploadSuccess(fileId: string, fileName: string) => void-Callback when file upload completes successfully
filenamestring-Optional custom filename to use instead of the uploaded file's name
allowedFileTypesstring[]-Optional array of allowed file types (e.g., '.jpg', 'image/*')
autoProceedbooleanfalseWhether to start upload automatically when files are selected

The component provides:

  • Drag and drop interface
  • File type restrictions
  • Upload progress bar
  • Automatic or manual upload triggering
  • Integration with Vysta's file service

License

MIT

1.0.47

6 months ago

1.0.46

6 months ago

1.0.45

7 months ago

1.0.44

7 months ago

1.0.43

7 months ago

1.0.42

7 months ago

1.0.41

7 months ago

1.0.40

7 months ago

1.0.39

7 months ago

1.0.38

7 months ago

1.0.37

7 months ago

1.0.36

7 months ago

1.0.35

7 months ago

1.0.34

8 months ago

1.0.33

9 months ago

1.0.32

9 months ago

1.0.31

9 months ago

1.0.30

9 months ago

1.0.29

9 months ago

1.0.28

9 months ago

1.0.27

9 months ago

1.0.26

9 months ago

1.0.25

10 months ago

1.0.24

10 months ago

1.0.23

10 months ago

1.0.22

10 months ago

1.0.21

10 months ago

1.0.20

10 months ago

1.0.19

10 months ago

1.0.18

10 months ago

1.0.17

10 months ago

1.0.16

10 months ago

1.0.15

10 months ago

1.0.14

11 months ago

1.0.13

11 months ago

1.0.12

11 months ago

1.0.11

11 months ago

1.0.10

11 months ago

1.0.9

11 months ago

1.0.8

11 months ago

1.0.7

11 months ago

1.0.6

11 months ago

1.0.5

11 months ago

1.0.4

11 months ago

1.0.3

11 months ago

1.0.2

11 months ago

1.0.1

11 months ago

1.0.0

11 months ago