chemical_docs/pages/IntroductionPage.tsx
2025-06-13 18:13:43 +02:00

220 lines
7.3 KiB
TypeScript

import React from 'react';
import { ContentPage } from '../components/ContentPage';
import { CodeBlock } from '../components/CodeBlock';
export const IntroductionPage: React.FC = () => {
const intro = (
<>
<p className="mb-4">
Chemical is a Luau library designed for building applications with a reactive programming paradigm, primarily within environments like Roblox. It provides tools for managing state, creating derived data, handling side effects, and composing user interfaces in a declarative way.
</p>
<p className="mb-4">
Inspired by modern frontend frameworks, Chemical aims to simplify complex state interactions and UI logic by providing a structured and predictable way to handle data flow.
</p>
</>
);
const sections = [
{
title: "Key Features",
entries: [
{
signature: "Reactive Primitives",
description: "Includes `Value`, `Computed`, and `Effect` for fine-grained reactive control.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Computed = Chemical.Computed
local Effect = Chemical.Effect
-- Create a reactive value
local count = Value(0)
-- Create a computed value derived from count
local doubled = Computed(function()
return count:get() * 2
end)
-- Create an effect that runs when count changes
Effect(function()
print("Count changed to:", count:get())
print("Doubled is:", doubled:get())
end)
count:set(5) -- This will trigger the effect
-- Output:
-- Count changed to: 5
-- Doubled is: 10
`,
},
{
signature: "Stateful Collections",
description: "Manage lists and dictionaries with `Table` and `Map`, which propagate changes reactively.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Table = Chemical.Table
local Effect = Chemical.Effect
local items = Table({"apple", "banana"})
Effect(function()
print("Items:", table.concat(items:get(), ", "))
end)
items:insert("cherry")
-- Output:
-- Items: apple, banana
-- (After scheduler runs)
-- Items: apple, banana, cherry
`,
},
{
signature: "Declarative UI Composition",
description: "Use the `Compose` API to build and update UI elements based on reactive state.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Computed = Chemical.Computed
local Compose = Chemical.Compose
local Children = Chemical.Symbols.Children -- Alias for Children key
-- Assuming playerGui is defined: local playerGui = game.Players.LocalPlayer:WaitForChild("PlayerGui")
local name = Value("Player")
local score = Value(100)
local MyScreenGui = Compose("ScreenGui")({
Parent = playerGui,
[Children] = { -- Use the aliased Children symbol
Compose("TextLabel")({
Name = "PlayerName",
Position = UDim2.new(0.5, 0, 0.1, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
Text = Computed(function()
return "Name: " .. name:get()
end),
TextColor3 = Color3.new(1,1,1),
BackgroundTransparency = 1,
Size = UDim2.new(0, 200, 0, 30)
}),
Compose("TextLabel")({
Name = "PlayerScore",
Position = UDim2.new(0.5, 0, 0.2, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
Text = Computed(function()
return "Score: " .. score:get()
end),
TextColor3 = Color3.new(1,1,1),
BackgroundTransparency = 1,
Size = UDim2.new(0, 200, 0, 30)
})
}
})
-- Later, when state changes, the UI updates automatically
name:set("Hero")
score:set(150)
`,
},
{
signature: "Networked State with Reactors",
description: "Synchronize state across server and clients using `Reactor` and `Reaction` objects for multiplayer experiences.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Reactor = Chemical.Reactor
local Effect = Chemical.Effect
-- Server-side
local PlayerDataReactor = Reactor({ Name = "PlayerData" }, function(key_playerIdString, initialData)
-- 'key_playerIdString' is the key, typically not stored in this returned table.
initialData = initialData or {}
return {
Health = Value(initialData.Health or 100),
Mana = Value(initialData.Mana or 50)
}
end)
local player1DataReaction = PlayerDataReactor:create("player1_id", { Health = 90 })
-- Access properties directly on the reaction:
player1DataReaction.Health:set(80) -- Change will replicate
-- Client-side
local player1DataClient = PlayerDataReactor:await("player1_id")
-- player1DataClient is now the Reaction object
if player1DataClient then
Effect(function()
print("Player 1 Health:", player1DataClient.Health:get())
end)
end
`,
},
],
},
{
title: "Philosophy",
entries: [
{
signature: "Data Flow",
description: "Chemical promotes a unidirectional data flow. State changes trigger computations, which in turn can trigger effects (like UI updates or network calls). This makes applications easier to reason about.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Computed = Chemical.Computed
local Effect = Chemical.Effect
-- State (Value) -> Derived State (Computed) -> Side Effect (Effect)
local function UpdateDisplay(text) print("Display: " .. text) end
local price = Value(10)
local quantity = Value(2)
local totalCost = Computed(function()
return price:get() * quantity:get()
end)
Effect(function()
UpdateDisplay("Total Cost: $" .. totalCost:get())
end)
price:set(12) -- Triggers totalCost recomputation, then updates display
quantity:set(3) -- Triggers totalCost recomputation, then updates display
`
},
{
signature: "Modularity",
description: "The library is structured with clear separation of concerns: core reactivity, state management, UI composition, and networking are distinct but interoperable parts.",
example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
-- Assuming modules are set up in ReplicatedStorage.Chemical.Modules
-- local PlayerProfileReactor = require(game.ReplicatedStorage.Chemical.Modules.PlayerProfileReactor) -- Uses Reactor
-- local HUD = require(game.ReplicatedStorage.Chemical.Modules.HUDComponent) -- Uses Compose
-- Mock for example purposes
local PlayerProfileReactor = {
await = function(playerId)
print("Mock Reactor: Awaiting profile for", playerId)
-- Simulate awaiting a profile by returning it directly for example simplicity
-- In a real scenario, this would yield until the data is available.
return {
UserId = playerId,
Name = Chemical.Value("MockPlayer-" .. playerId:sub(1,4))
}
end
}
local HUD = { Render = function(profile) print("Rendering HUD for", profile.Name:get()) end }
-- End Mock
local player = game.Players.LocalPlayer
local profileReaction = PlayerProfileReactor:await(tostring(player.UserId))
if profileReaction then
HUD.Render(profileReaction) -- HUD component consumes reactive profile data
end
`
}
]
}
];
return <ContentPage title="Introduction to Chemical" introduction={intro} sections={sections} />;
};