Skip to main content
Data display components help you present information to users in organized, interactive ways. These components handle complex data structures, pagination, sorting, and user interactions.

Dialog

The dialog component creates modal dialogs for displaying content, confirmations, alerts, and prompts.
packages/loopar/src/components/dialog.jsx
const MetaDialog = (props) => {
  const [open, setOpen] = useState(props.open || false);
  
  const handleSetOpenClose = (open) => {
    loopar.handleOpenCloseDialog(props.id, open);
    if(open) props.onOpen && props.onOpen();
    if(!open) props.onClose && props.onClose();
  };

  const sizes = {
    sm: "md:min-w-[45%] lg:min-w-[40%] xl:min-w-[35%]",
    md: "md:min-w-[60%] lg:min-w-[50%] xl:min-w-[45%]",
    lg: "md:min-w-[75%] lg:min-w-[70%] xl:min-w-[60%]",
    full: "min-w-[100%] min-h-[100%] max-w-[100%] max-h-[100%]",
  };

  return (
    <DialogContextProvider>
      <Dialog open={open} onOpenChange={handleSetOpenClose} key={props.id}>
        <DialogContent className={`sm:max-w-md ${sizes[props.size || "sm"]} flex flex-col`}>
          <DialogHeader>
            <DialogTitle className="flex space-x-2">
              {props.type && <Icon type={props.type} size={48} />}
              <h2 className="text-2xl">{props.title}</h2>
            </DialogTitle>
          </DialogHeader>
          <DialogDescription className="overflow-auto max-h-[80vh]">
            {content}
          </DialogDescription>
          {props.hasFooter !== false && (
            <DialogFooter>
              {(getButtons() || []).map((b) => (
                <Button
                  key={b.name}
                  variant={b.variant || "primary"}
                  onClick={(e) => {
                    e.preventDefault();
                    b.dismiss && setDialogOpen(false);
                    b.onClick();
                  }}
                >
                  {b.content || b.text || b.label}
                </Button>
              ))}
            </DialogFooter>
          )}
        </DialogContent>
      </Dialog>
    </DialogContextProvider>
  );
};

Dialog Types

import { Alert } from "@components/dialog";

<Alert
  title="Warning"
  message="This action cannot be undone."
  type="alert"
  ok={() => console.log("OK clicked")}
/>

Properties

title
string
required
Dialog title
message
string
Dialog content (for simple text)
children
ReactNode
Dialog content (for complex JSX)
type
string
Dialog type: alert, confirm, info, error, prompt
size
string
default:"sm"
Dialog size: sm, md, lg, full
open
boolean
default:false
Control dialog visibility
buttons
array
Custom button configuration:
{
  name: string,
  text: string,
  variant: "primary" | "secondary" | "destructive",
  onClick: () => void,
  dismiss?: boolean
}[]
ok
function
OK button callback
cancel
function
Cancel button callback (for confirm dialogs)
Show/hide footer with buttons

Programmatic Usage

import loopar from "loopar";

// Simple alert
loopar.alert("Operation completed successfully!");

// Confirm dialog
loopar.confirm("Delete this record?", () => {
  // User confirmed
  deleteRecord();
});

// Custom dialog
loopar.dialog({
  title: "Custom Dialog",
  size: "lg",
  content: <CustomComponent />,
  buttons: [
    {
      name: "save",
      text: "Save",
      variant: "primary",
      onClick: () => save()
    },
    {
      name: "cancel",
      text: "Cancel",
      variant: "secondary",
      onClick: () => console.log("Cancelled")
    }
  ]
});

Table / Form Table

The form_table component creates editable tables with drag-and-drop row reordering, inline editing, and bulk operations.
packages/loopar/src/components/form-table.jsx
const FormTable = (props) => {
  const { selectedRows, bulkRemove, baseColumns, selectorCol, rows } = useTable();
  const { register, move, remove, append } = props;

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(TouchSensor, { activationConstraint: { delay: 100, tolerance: 5 } }),
    useSensor(MouseSensor, { activationConstraint: { delay: 100, tolerance: 5 } })
  );

  const onDragEnd = useCallback((event) => {
    const { active, over } = event;
    if (active.id !== over.id) {
      const oldIndex = rows.findIndex(f => f.id === active.id);
      const newIndex = rows.findIndex(f => f.id === over.id);
      move(oldIndex, newIndex);
    }
  }, [rows, move]);

  return (
    <div className="feed py-2">
      <DndContext
        sensors={sensors}
        collisionDetection={closestCenter}
        onDragEnd={onDragEnd}
      >
        <SortableContext
          items={rows.map(f => f.id)}
          strategy={verticalListSortingStrategy}
        >
          <BaseTable
            viewType={"List"}
            hasPagination={false}
            hasHeaderOptions={true}
            columns={mappedColumns}
            rowTemplate={SortableRow}
          />
        </SortableContext>
      </DndContext>
      <div className="flex flex-row gap-2">
        {selectedRows.length > 0 && (
          <Button
            type="button"
            variant="destructive"
            onClick={e => {
              e.preventDefault();
              bulkRemove(false);
            }}
          >
            <Trash2Icon className="mr-1" size={16} />
            Remove
          </Button>
        )}
        <Button
          type="button"
          variant="primary"
          onClick={handleAppend}
        >
          <PlusIcon className="mr-1" size={16} />
          Add row
        </Button>
      </div>
    </div>
  )
}

Properties

data.name
string
required
Field name for the table data
data.label
string
Table label
__META__
object
required
Entity metadata defining columns:
{
  elements: [
    {
      element: "input",
      data: {
        name: "item_name",
        label: "Item",
        in_list_view: true
      }
    },
    {
      element: "input",
      data: {
        name: "quantity",
        label: "Qty",
        format: "int",
        in_list_view: true
      }
    }
  ]
}

Features

  • Drag & Drop: Reorder rows by dragging
  • Inline Editing: Edit cells directly in the table
  • Bulk Selection: Select multiple rows for bulk operations
  • Dynamic Rows: Add/remove rows on the fly
  • Validation: Per-cell validation using field definitions

Example

{
  "element": "form_table",
  "data": {
    "name": "items",
    "label": "Order Items"
  }
}

Carrusel

The carrusel component creates an image carousel with automatic sliding and navigation controls.
packages/loopar/src/components/carrusel.jsx
export default function Carrusel(props){
  const { designerMode, updateElements } = useDesigner();
  
  const data = props.data || {};
  const defaultElements = [
    {
      element: "banner",
      data: {
        key: `${data.key}-1`,
        label: "Slider 1..",
        text: "Slide 1..",
        color_overlay: "rgba(0,0,0,0.3)",
        background_image: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920&q=80"
      },
    },
    {
      element: "banner",
      data: {
        key: `${data.key}-2`,
        label: "Slider 2..",
        text: "Slide 2..",
        color_overlay: "rgba(0,0,0,0.3)",
        background_image: "https://images.unsplash.com/photo-1551434678-e076c223a692?w=1920&q=80",
      },
    },
  ];

  return (
    <Preassembled
      {...props}
      notDroppable={true} 
      defaultElements={defaultElements}
    >
      <BaseCarrusel
        {...props}
        addSlide={addSlide}
      />
    </Preassembled>
  );
}

Properties

data.key
string
required
Unique identifier for the carousel
data.autoplay
boolean
default:true
Enable automatic sliding
data.interval
number
default:5000
Slide interval in milliseconds
data.show_controls
boolean
default:true
Show navigation arrows
data.show_indicators
boolean
default:true
Show slide indicators (dots)

Example

{
  "element": "carrusel",
  "data": {
    "key": "hero-carousel",
    "autoplay": true,
    "interval": 5000
  },
  "elements": [
    {
      "element": "banner",
      "data": {
        "key": "slide-1",
        "label": "Welcome",
        "text": "Slide 1 Description",
        "background_image": "/images/slide1.jpg",
        "color_overlay": "rgba(0,0,0,0.4)"
      }
    },
    {
      "element": "banner",
      "data": {
        "key": "slide-2",
        "label": "Features",
        "text": "Slide 2 Description",
        "background_image": "/images/slide2.jpg",
        "color_overlay": "rgba(0,0,0,0.4)"
      }
    }
  ]
}
The gallery component displays a grid of images with lightbox functionality.

Properties

data.columns
number
default:3
Number of columns in the grid
data.gap
number
default:2
Gap between images (in rem)
data.images
array
required
Array of image objects:
{
  src: string,
  alt?: string,
  title?: string
}[]

Example

{
  "element": "gallery",
  "data": {
    "key": "product-gallery",
    "columns": 4,
    "gap": 1,
    "images": [
      {
        "src": "/images/product1.jpg",
        "alt": "Product 1"
      },
      {
        "src": "/images/product2.jpg",
        "alt": "Product 2"
      }
    ]
  }
}

Slider

The slider component provides a range input for selecting numeric values.

Properties

data.name
string
required
Field name
data.label
string
required
Slider label
data.min
number
default:0
Minimum value
data.max
number
default:100
Maximum value
data.step
number
default:1
Step increment

Example

{
  "element": "slider",
  "data": {
    "name": "volume",
    "label": "Volume",
    "min": 0,
    "max": 100,
    "step": 5
  }
}

Document History

The document_history component displays a timeline of changes to a document.

Usage

import DocumentHistory from "@components/document-history";

<DocumentHistory
  documentName="User"
  documentId="user-123"
/>

No Data

The no_data component displays a message when no data is available.

Usage

import NoData from "@components/no-data";

<NoData
  message="No records found"
  icon="search"
/>

Table Context

For advanced table usage, use the TableProvider and useTable hook:
import { TableProvider, useTable } from "@components/table/TableContext";

const MyTable = () => {
  const { 
    rows, 
    selectedRows, 
    selectRow, 
    selectAll,
    bulkRemove 
  } = useTable();

  return (
    // Your table implementation
  );
};

<TableProvider
  initialDocument={Document}
  docRef={docRef}
  rows={data}
>
  <MyTable />
</TableProvider>

Best Practices

Performance

  • Use pagination for large datasets
  • Implement virtual scrolling for very long tables
  • Lazy load images in galleries and carousels
  • Debounce search and filter operations

Accessibility

  • Provide alt text for all images
  • Ensure keyboard navigation works in tables and carousels
  • Use ARIA labels for dialog buttons
  • Maintain focus management in dialogs

User Experience

  • Show loading states during data fetching
  • Provide clear feedback for bulk operations
  • Include empty states with helpful messages
  • Enable undo for destructive operations

Next Steps

Form Components

Collect data with form inputs

Custom Components

Build your own display components

Build docs developers (and LLMs) love