🎨 Fractal Platform Layout Guide

Master custom layouts and UI design

1. Introduction to UI Design in Fractal Platform

The Fractal Platform Approach

Fractal Platform is designed with a unique philosophy: you focus on your database and business logic first, and the UI is automatically generated as a "skin" over your data model. This means you get a working interface for free, without spending time on initial UI development.

Key Benefits

πŸ’‘ Why This Matters:
  • Rapid Development: Get a working application without UI coding
  • Data-Driven: Your UI automatically reflects your data structure
  • Progressive Enhancement: Start with standard UI, customize later
  • Flexible Customization: From simple tweaks to complete custom designs

The UI Development Journey

Most applications follow this progression:

Stage 1: Standard Auto-Generated UI
   ↓
Stage 2: Dimension-Based Customization
   ↓
Stage 3: Custom Layouts for Key Screens
   ↓
Stage 4: Fully Custom Components
πŸ“ Real-World Example:

Building an Online Shop:

  • Day 1: Create product database β†’ Get admin panel for free
  • Week 1: Add UI dimensions for better forms
  • Week 2: Create custom layout for product catalog
  • Week 3: Build custom checkout component

2. External vs Internal Screens

Understanding Screen Types

It's recommended to split your application's UI into two categories:

External Screens

  • Seen by all users
  • Represent your brand
  • Need custom design
  • Require HTML designers

Internal Screens

  • Admin and staff only
  • Functional priority
  • Can use standard UI
  • Save development time

Example: Online Shop

πŸ›’ External Screens (Need Custom Design):
  • Main catalog with product grid
  • Product detail page
  • Shopping cart and checkout
πŸ”§ Internal Screens (Can Use Standard UI):
  • Admin panel for managing products
  • Order processing dashboard
  • Report generation system
πŸ’‘ Pro Tip: Start by using standard UI for ALL screens. Identify which screens your users actually see, then prioritize custom design for those external screens only.

3. Standard UI Interface

How Standard UI Works

The system analyzes your document structure and automatically creates appropriate UI controls:

Data Type UI Control Example
String, Number TextBox with Label "Name": "Bob" β†’ Name: [Bob____]
Boolean CheckBox with Label "Active": true β†’ β˜‘ Active
Enum ComboBox with Label "Status" β†’ Status: [Select β–Ό]
Object GroupBox "Address": {...} β†’ [Address Group]
Array Grid "Items": [...] β†’ [Data Grid]
Deep Nesting (4+ levels) TextBox with [E] button Opens nested form

Document to UI Example

{
  "Name": "Bob",
  "Age": 25,
  "IsActive": true,
  "Address": {
    "Street": "Main St",
    "City": "New York"
  },
  "Orders": [
    { "ID": 1, "Total": 100 },
    { "ID": 2, "Total": 150 }
  ]
}

This JSON automatically becomes:

Name:      [Bob________]
Age:       [25_________]
β˜‘ IsActive

[Address]
  Street:  [Main St____]
  City:    [New York___]

Orders:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ID β”‚ Total β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1  β”‚ 100   β”‚
β”‚ 2  β”‚ 150   β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘ Key Point: All controls are placed vertically, one under another, by default. Nested objects create grouped sections or sub-forms.

4. UI Customization with Dimensions

What are Dimensions?

Dimensions are configuration documents that customize the Standard UI without writing HTML or code. They're the easiest way to improve your interface.

Available Dimensions

Dimension Purpose Example Use
UI Basic UI information Set control types, locations, layouts
Enum Define dropdown options Status: Draft, Published, Archived
Validation Restrict data types Email format, number ranges
Menu Add navigation menus Action buttons on forms
ToolTip Add help text Field instructions for users
Edit Control editability Make fields read-only
Pagination Multi-page forms Split long forms into pages
Tab Tabbed interface Group related fields
Wizard Step-by-step forms Onboarding, registration
Filter Add search box Filter grid data
Theme Color schemes Dark mode, light mode
Language Localization Multi-language support

Example: Using UI Dimension

{
  "Layout": "MyCustomLayout",
  "Title": {
    "ControlType": "Label",
    "Location": "HeaderArea"
  },
  "Description": {
    "ControlType": "RichTextBox",
    "Width": 500,
    "Height": 200
  },
  "Status": {
    "ControlType": "ComboBox"
  }
}
πŸ’‘ Best Practice: Start with dimensions for quick wins (tooltips, validation, menus) before moving to custom layouts.

5. Creating Custom Layouts

What is a Layout?

A layout is an HTML container that defines where your controls appear on the page. Instead of the default vertical stack, you create your own HTML structure.

Two Types of Custom Layouts

Type 1: Embedded Layout

Part of Standard UI with header/footer

{
  "Layout": "MyLayout"
}

βœ… Keep filter, buttons, theme selector
βœ… Override only center content

Type 2: Raw Layout

Complete custom HTML page

{
  "Layout": "MyLayout",
  "IsRawPage": true
}

βœ… Full design control
⚠️ Must include necessary scripts

Creating Your First Layout

Step 1: Create HTML file (MyLayout.html)

Welcome to My App!

This is a custom layout

Step 2: Configure UI Dimension

{
  "Layout": "MyLayout",
  "IsRawPage": false
}
πŸ’‘ File Location: Place HTML files in your application's Layout folder. The system automatically finds them by name.

Layout with Visual Example

πŸ“ Standard Layout vs Custom Layout:
STANDARD LAYOUT          CUSTOM LAYOUT
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Filter   [?] β”‚         β”‚  HERO IMAGE  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”‚   Welcome!   β”‚
β”‚ Name: [____] β”‚         β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Age:  [____] β”‚         β”‚ Name:  Age:  β”‚
β”‚ β˜‘ Active     β”‚         β”‚ [___]  [___] β”‚
β”‚              β”‚         β”‚              β”‚
β”‚ [Save][Cancelβ”‚         β”‚ β˜‘ Active     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚ [Save] [___] β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. Control Tags and Placement

Method 1: Using UI Dimension Locations

Define control locations in UI Dimension, reference them in HTML:

Document:

{
  "Title": "Hello World!",
  "Subtitle": "Welcome to my app"
}

UI Dimension:

{
  "Layout": "MyLayout",
  "Title": {
    "ControlType": "Label",
    "Location": "TitleLocation"
  },
  "Subtitle": {
    "ControlType": "Label",
    "Location": "SubtitleLocation"
  }
}

MyLayout.html:

@Value

Method 2: Direct Control Tags

Place controls directly in HTML without locations:

UI Dimension:

{
  "Layout": "MyLayout",
  "IsRawPage": true
}

MyLayout.html:

  

@Value

πŸ’‘ Tip: Method 2 is simpler but gives you less control from the UI Dimension. Choose based on your needs.

Repeating Controls (Arrays)

Use repeatable="true" to loop through arrays:

Document:

{
  "Products": [
    { "Name": "Laptop", "Price": 999 },
    { "Name": "Mouse", "Price": 25 },
    { "Name": "Keyboard", "Price": 75 }
  ]
}

Layout:

Result:

Laptop

$999

Mouse

$25

Keyboard

$75

Nested Repeatable Controls


            

Injecting Standard Controls

Use type="standard" to render built-in controls:


⚠️ Note: Standard controls must be configured in the UI Dimension with appropriate ControlType (e.g., "Tags", "RichTextBox").

7. Control Properties Reference

Basic Properties

Property Description Example
@Value Control's value
@Name Control's name
@Key Attribute key
@ToolTip Help text

State Properties

Property Returns Usage
@Enabled "true" or "false" if (@Enabled == "true")
@Disabled "disabled" or ""
@Checked "checked" or ""
@ReadOnly "readonly" or ""
@Visible "true" or "false" if (@Visible == "true")

Dimension Properties

Property Description
@Width Control width from UI Dimension
@Height Control height from UI Dimension

URL and Script Properties

Property Description Example
@ClickUrl Button click URL
@ClickSubmit Button submit script
@ChangeSubmit Change event script

Grid/Array Properties

Property Description
@AddRowUrl URL to add new row
@EditRowUrl URL to edit row
@DeleteRowUrl URL to delete row
@AddRowScript Script to add row
@EditRowScript Script to edit row
@DeleteRowScript Script to delete row

Page Action Properties

Property Description
@SavePageScript Save button script
@CancelPageScript Cancel button script
@RefreshPageScript Refresh button script
@NextPageScript Next page script (pagination)
@PrevPageScript Previous page script
@SavePageUrl Save button URL
@CancelPageUrl Cancel button URL

Context Properties

Property Description Example Value
@UserName Current user's name "john.doe"
@UserLanguage User's language "EN", "UA", "RU"
@UserTheme User's theme "Dark", "Light"
@UserAvatar User's avatar URL "/files/avatar.png"
@AppName Application name "MyApp"
@FormName Current form name "ProductEdit"

URL Properties

Property Description
@BaseUrl Base URL of platform
@BaseAppUrl Base URL of application
@BaseFormUrl Base URL with form name
@BaseFilesUrl URL to files folder

Helper Properties

Property Description
@StartForm Render form start tags
@EndForm Render form end tags
@Styles Inject custom styles
@ComboBoxItems Render dropdown options
@FilterText Current filter text

Special Accessors

@Top.FieldName.Value     // Access top-level field from nested context
@FieldName.Computed      // Compute element value
@FieldName.TextBox       // Render as textbox attributes
@FieldName.Button        // Render as button attributes
@FieldName.Link          // Render as link attributes
@FieldName.CheckBox      // Render as checkbox attributes
@FieldName.Picture       // Render as image attributes

Example: Complete Form Template

@StartForm

@FormName

@EndForm

8. Custom Controls and Components

Understanding Components

A component is a UI control that represents a segment (subset) of your JSON document, usually containing multiple fields. Components allow you to create rich, interactive controls beyond simple textboxes and checkboxes.

πŸ’‘ Standard vs Component:
  • Standard Control: Based on ONE field (TextBox, CheckBox)
  • Component: Based on MULTIPLE fields (Tags, Calendar, RichEditor)

Creating a Custom Component: Tags Example

Step 1: Document Structure

{
  "Tags": ["Design", "Development", "Marketing"]
}

Step 2: UI Dimension

{
  "Tags": {
    "ControlType": "Tags"
  }
}

Step 3: Override RenderForm

public class MyRenderForm : BaseRenderForm
{
    public override string RenderComponent(ComponentDOMControl domControl)
    {
        if (domControl.Component == "Tags")
        {
            return RenderTags(domControl);
        }
        
        return base.RenderComponent(domControl);
    }
    
    private string RenderTags(ComponentDOMControl domControl)
    {
        var tags = domControl.GetValue() as List;
        var html = new StringBuilder();
        
        html.Append("
"); html.Append($""); foreach (var tag in tags) { html.Append($"{tag} "); } html.Append(""); html.Append("
"); return html.ToString(); } }

The Hidden Input Pattern

⚠️ Important Pattern:

Components use a hidden input field to communicate between frontend and backend:

Your JavaScript must update this hidden field before form submission. The backend reads this JSON and applies it to your document.

Component JavaScript Example

Alternative: Using Standard Controls

For simpler components, you can compose them from standard controls instead of writing custom JavaScript:

public class MyRenderForm : BaseRenderForm
{
    public override string RenderComponent(ComponentDOMControl domControl)
    {
        if (domControl.Component == "AddressCard")
        {
            var html = new StringBuilder();
            
            // Render standard controls inside component
            html.Append("
"); html.Append(RenderTextBox(domControl.GetControl("Street"))); html.Append(RenderTextBox(domControl.GetControl("City"))); html.Append(RenderTextBox(domControl.GetControl("ZipCode"))); html.Append("
"); return html.ToString(); } return base.RenderComponent(domControl); } }
πŸ’‘ When to Use Each Method:
  • Hidden Input Pattern: Complex interactions, custom UI widgets
  • Standard Controls: Simple layouts, form sections, grouped fields

Built-in Components

Check available standard components at: http://booben.com/jupiter/?app=ControlsGallery

πŸ“š Common Standard Components:
  • Tags - Tag input control
  • Calendar - Date picker
  • RichTextBox - WYSIWYG editor
  • Upload - File uploader
  • TreeView - Hierarchical data
  • RadioButton - Radio group

9. Advanced: RenderForm Customization

Understanding RenderForm

The RenderForm class is responsible for converting your data and UI dimensions into actual HTML. By creating a custom RenderForm class, you can override how any control renders.

Creating Custom RenderForm

Step 1: Create Your Class

public class MyRenderForm : BaseRenderForm
{
    // Override methods here
}

Step 2: Register in Application

public class MyApplication : Application
{
    public override BaseRenderForm CreateRenderForm()
    {
        return new MyRenderForm();
    }
}

Key Methods to Override

Method Purpose
RenderStyles() Inject custom CSS
RenderTextBox() Customize textbox rendering
RenderButton() Customize button rendering
RenderCheckBox() Customize checkbox rendering
RenderComboBox() Customize dropdown rendering
RenderGrid() Customize grid/table rendering
RenderGroupBox() Customize group container
RenderComponent() Customize component rendering
Render() Override entire form rendering

Example: Custom TextBox

public override string RenderTextBox(TextBoxDOMControl domControl)
{
    var html = new StringBuilder();
    
    html.Append($"
"); html.Append($" "); html.Append($" "); html.Append($"
"); return html.ToString(); }

Example: Custom Grid

public override string RenderGrid(GridDOMControl domControl)
{
    var html = new StringBuilder();
    
    html.Append("
"); html.Append($""); html.Append(""); html.Append(" "); // Render headers foreach (var column in domControl.Columns) { html.Append($""); } html.Append(""); html.Append(" "); html.Append(" "); for (uint i = 0; i < domControl.RowCount; i++) { html.Append(""); foreach (var column in domControl.Columns) { html.Append($""); } html.Append(""); html.Append(""); } html.Append(" "); html.Append("
{column.Label}Actions
{domControl.GetCellValue(i, column.Name)}"); html.Append($" "); html.Append($" "); html.Append("
"); html.Append("
"); return html.ToString(); }

Useful BaseRenderForm Properties

Property Description
Application Reference to Application instance
DOMForm The form being rendered
Collection Data source for the form

Helper Methods

// Get localized string
string localText = GetLocalizedValue("Hello World");

// Get tooltip
string tooltip = GetToolTip(domControl);

// Get full file URL
string imageUrl = GetFileUrl("avatar.png");
// Returns: booben.com/Jupiter/MyApp/files/avatar.png

// Get validation error style
string errorStyle = GetValidationErrorStyle(domControl);

Example: Adding Custom Menu

public override string RenderMenu(DOMControl domControl)
{
    var html = new StringBuilder();
    
    html.Append("
"); html.Append($" View Details"); html.Append($" Quick Edit"); html.Append(" Export"); html.Append("
"); return html.ToString(); }
πŸ’‘ Best Practice: Always call base.RenderXXX() for controls you don't want to customize. This ensures default behavior for other controls.

Complete Example: Custom Product Form

public class ShopRenderForm : BaseRenderForm
{
    public override string RenderStyles()
    {
        return @"
            
        ";
    }
    
    public override string RenderPicture(PictureDOMControl domControl)
    {
        if (domControl.Name == "ProductImage")
        {
            return $@"
                
Product
"; } return base.RenderPicture(domControl); } public override string RenderTextBox(TextBoxDOMControl domControl) { if (domControl.Name == "Price") { return $@"
@ChangeSubmit Change event script lt;/span>
"; } return base.RenderTextBox(domControl); } }
⚠️ Performance Note: RenderForm methods are called frequently. Keep logic simple and avoid heavy processing. Cache values when possible.