Add 'Responsive Systems' guide and remove 'Reactive System' guide
This commit is contained in:
@@ -92,6 +92,7 @@ export default defineConfig({
|
||||
{ text: 'XML', link: '/guides/xml' },
|
||||
{ text: 'Canvas', link: '/guides/canvas'},
|
||||
{ text: 'Element Loading', link: '/guides/element-loading' },
|
||||
{ text: 'Responsive Systems', link: '/guides/responsive-system' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# Reactive System
|
||||
|
||||
The reactive system in Basalt allows you to create dynamic property values using expressions. These expressions automatically update when related values change.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```lua
|
||||
local basalt = require("basalt")
|
||||
|
||||
-- Create a simple label and center it horizontally
|
||||
local main = basalt.getMainFrame()
|
||||
:addLabel()
|
||||
:setText("Hello World")
|
||||
:setX("{parent.width / 2 - self.width / 2}") -- Centers the label horizontally
|
||||
|
||||
-- Create a progress bar that takes 80% of parent width
|
||||
main:addProgressbar()
|
||||
:setWidth("{parent.width * 0.8}")
|
||||
:setX("{parent.width * 0.1}") -- 10% margin on both sides
|
||||
```
|
||||
|
||||
## Available Variables
|
||||
|
||||
In reactive expressions, you have access to:
|
||||
|
||||
- `self` - The current element
|
||||
- `parent` - The parent element
|
||||
- `elementName` - A given name of a element
|
||||
- Any property of these elements (width, height, value, text, etc.)
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Responsive Layout
|
||||
|
||||
```lua
|
||||
local frame = basalt.getMainFrame()
|
||||
:addFrame()
|
||||
:setSize("{parent.width * 0.5}", "{parent.height * 0.5}") -- Takes half of parent size
|
||||
:setPosition("{parent.width / 2 - self.width / 2}", -- Centers the frame
|
||||
"{parent.height / 2 - self.height / 2}")
|
||||
```
|
||||
|
||||
### Dynamic Sizing
|
||||
|
||||
```lua
|
||||
local label = frame:addLabel()
|
||||
:setText("Dynamic width based on text")
|
||||
:setWidth("{#self.text}") -- Width equals text length
|
||||
```
|
||||
|
||||
### Linked Properties
|
||||
|
||||
```lua
|
||||
local slider = frame:addSlider("mySlider")
|
||||
:setPosition(2, 2)
|
||||
:setSize(10, 1)
|
||||
|
||||
local progress = frame:addProgressBar()
|
||||
:setPosition(2, 4)
|
||||
:setSize(10, 1)
|
||||
:setProgress("{mySlider.value}") -- Progress syncs with slider value
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
- Use curly braces `{}` to define a reactive expression
|
||||
- Basic math operators are supported (+, -, *, /, %)
|
||||
- Access properties using dot notation (element.property)
|
||||
- Can use basic functions and comparisons
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Keep expressions simple and readable
|
||||
2. Use meaningful variable names in complex calculations
|
||||
3. Test expressions with different parent sizes
|
||||
4. Consider edge cases (minimum/maximum sizes)
|
||||
218
docs/guides/responsive-system.md
Normal file
218
docs/guides/responsive-system.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Dynamic & Reactive Systems
|
||||
|
||||
Basalt provides three systems for creating dynamic, responsive UIs: the **State System**, the **Responsive Plugin**, and the **Reactive Plugin**. Each serves a different purpose and can be combined for maximum flexibility.
|
||||
|
||||
---
|
||||
|
||||
## State System
|
||||
|
||||
The state system allows elements to have conditional states that automatically activate based on custom logic. This is the foundation that powers the responsive plugin.
|
||||
|
||||
### Conditional States
|
||||
|
||||
Register states that evaluate conditions and automatically activate/deactivate:
|
||||
|
||||
```lua
|
||||
-- Using a function
|
||||
-- When using functions, you need to manually specify which properties to observe
|
||||
local label = main:addLabel()
|
||||
:registerResponsiveState("small", function(self)
|
||||
return self.parent:getWidth() < 25
|
||||
end, {
|
||||
observe = {
|
||||
{main, "width"} -- Format: {element, "property"}
|
||||
},
|
||||
})
|
||||
:setTextState("small", "Compact")
|
||||
:setText("Full Text")
|
||||
|
||||
-- Using a string expression (automatic dependency detection)
|
||||
-- String expressions automatically detect and observe referenced properties
|
||||
local sidebar = main:addFrame()
|
||||
:registerResponsiveState("collapsed", "parent.width < 30", 100)
|
||||
:setWidthState("collapsed", 5)
|
||||
:setWidthState("default", 15)
|
||||
:setY(15)
|
||||
```
|
||||
|
||||
### String Expressions
|
||||
|
||||
String expressions automatically parse and detect dependencies - no manual `observe` needed:
|
||||
|
||||
```lua
|
||||
-- Dependencies are auto-detected from the expression
|
||||
element:registerResponsiveState("portrait", "parent.width < parent.height", 100)
|
||||
```
|
||||
|
||||
You can optionally add extra dependencies that aren't in the expression:
|
||||
|
||||
```lua
|
||||
-- Auto-detects parent.width, but also observes otherElement.text
|
||||
element:registerResponsiveState("complex", "parent.width < 30", {
|
||||
priority = 100,
|
||||
observe = {
|
||||
{otherElement, "text"} -- Additional dependency
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Behind the scenes:** The system parses expressions like `"parent.width < 30"`, extracts property references (`parent.width`), and automatically sets up observers for reactive updates.
|
||||
|
||||
### Manual State Control
|
||||
|
||||
You can also control states manually:
|
||||
|
||||
```lua
|
||||
element:setState("customState", 100) -- Activate with priority 100
|
||||
element:unsetState("customState") -- Deactivate
|
||||
element:hasState("customState") -- Check if active
|
||||
element:getActiveStates() -- Get all active states sorted by priority
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Plugin
|
||||
|
||||
The responsive plugin builds on the state system to create layouts that adapt to parent size or other conditions. It provides a fluent builder API.
|
||||
|
||||
### Basic Responsive Layout
|
||||
|
||||
```lua
|
||||
local sidebar = main:addFrame()
|
||||
:responsive()
|
||||
:when("parent.width < 15")
|
||||
:apply({ width = 10, background = colors.gray })
|
||||
:when("parent.width >= 40")
|
||||
:apply({ width = 25, background = colors.lightGray })
|
||||
:otherwise({ width = 15 })
|
||||
```
|
||||
|
||||
### Advanced Conditions
|
||||
|
||||
String expressions support math operations and can reference any element property:
|
||||
|
||||
```lua
|
||||
local dynamicFrame = frame:addFrame()
|
||||
:responsive()
|
||||
:when("parent.width < parent.height")
|
||||
:apply({ width = "parent.width * 0.9", height = 10 })
|
||||
:otherwise({ width = 20, height = "parent.height * 0.9" })
|
||||
```
|
||||
|
||||
**Behind the scenes:** The responsive plugin automatically detects dependencies from expressions (like `parent.width`) and sets up observers for reactive updates.
|
||||
|
||||
---
|
||||
|
||||
## Reactive Plugin
|
||||
|
||||
The reactive plugin allows property values themselves to be dynamic expressions that automatically update when dependencies change.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```lua
|
||||
-- Center a label horizontally
|
||||
local label = frame:addLabel()
|
||||
:setText("Centered")
|
||||
:setX("{parent.width / 2 - self.width / 2}")
|
||||
|
||||
-- Progress bar that takes 80% of parent width
|
||||
frame:addProgressBar()
|
||||
:setWidth("{parent.width * 0.8}")
|
||||
:setX("{parent.width * 0.1}")
|
||||
```
|
||||
|
||||
### Available Variables
|
||||
|
||||
- `self` - The current element
|
||||
- `parent` - The parent element
|
||||
- `elementName` - Named elements (e.g., `mySlider.value`)
|
||||
|
||||
### Linked Properties
|
||||
|
||||
```lua
|
||||
local slider = frame:addSlider("volumeSlider")
|
||||
:setPosition(2, 2)
|
||||
|
||||
local label = frame:addLabel()
|
||||
:setText("{volumeSlider.value}") -- Text updates with slider
|
||||
:setX("{volumeSlider.x + volumeSlider.width + 2}")
|
||||
```
|
||||
|
||||
### Dynamic Sizing
|
||||
|
||||
```lua
|
||||
local label = frame:addLabel()
|
||||
:setText("Dynamic width")
|
||||
:setWidth("{#self.text + 2}") -- Width = text length + padding
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Combining Systems
|
||||
|
||||
You can combine all three systems for maximum flexibility:
|
||||
|
||||
```lua
|
||||
local frame = main:addFrame()
|
||||
:setWidth("{parent.width * 0.8}") -- Reactive: 80% of parent
|
||||
:responsive() -- Responsive: breakpoints
|
||||
:when("parent.width < 30")
|
||||
:apply({ background = colors.gray })
|
||||
:otherwise({ background = colors.lightGray })
|
||||
:setPropertyState("background", "hover", colors.white) -- State: hover effect
|
||||
```
|
||||
|
||||
The responsive and reactive plugins work together - you can use reactive expressions within `:apply()`:
|
||||
|
||||
```lua
|
||||
local element = main:addLabel()
|
||||
:responsive()
|
||||
:when("parent.width < 30")
|
||||
:apply({ text = "Small", x = "{parent.width - self.width}" }) -- Reactive expression
|
||||
:otherwise({ text = "Large", x = 5 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Practical Example: Adaptive Layout
|
||||
|
||||
A common use case is creating a layout that adapts between side-by-side and stacked views based on screen width:
|
||||
|
||||
```lua run
|
||||
local basalt = require("basalt")
|
||||
local main = basalt.getMainFrame()
|
||||
|
||||
-- Left container
|
||||
local rightContainer = main:addFrame()
|
||||
:setSize(20, 10)
|
||||
:setBackground(colors.green)
|
||||
:responsive()
|
||||
:when("parent.width >= 45") -- Wide: positioned next to left
|
||||
:apply({
|
||||
x = 24,
|
||||
y = 2,
|
||||
width = 20
|
||||
})
|
||||
:otherwise({
|
||||
x = 2,
|
||||
y = 13,
|
||||
width = "{parent.width - 3}"
|
||||
}) -- Narrow: positioned below left
|
||||
:done()
|
||||
|
||||
rightContainer:addLabel()
|
||||
:setText("Right Panel")
|
||||
:setPosition(2, 2)
|
||||
|
||||
-- Status label showing current layout mode
|
||||
local statusLabel = main:addLabel()
|
||||
:setPosition(2, 24)
|
||||
:responsive()
|
||||
:when("parent.width >= 45")
|
||||
:apply({ text = "Layout: Side by Side", foreground = colors.lime })
|
||||
:otherwise({ text = "Layout: Stacked", foreground = colors.orange })
|
||||
:done()
|
||||
basalt.run()
|
||||
```
|
||||
|
||||
When the main frame is wide (≥45 characters), the containers appear side by side. When it's narrow, they stack vertically - and the status label updates to reflect the current mode.
|
||||
Reference in New Issue
Block a user