الصفحات (Pages)
كيف تعمل الصفحات في بنية Orb - التوجيه، ربط السمات، الفتحات، والتنقل.
ذو صلة:
نظرة عامة
في Orb، الصفحة (Page) هي مسار يجمع السمات لعرض واجهة المستخدم. التركيبة الأساسية هي:
Orbital = Entity + Traits + Pages
بينما تحدد الكيانات البيانات والسمات السلوك، تحدد الصفحات أين يتفاعل المستخدمون مع النظام. الصفحات مدفوعة بالسمات - لا تحتوي على واجهة مستخدم مباشرة، بل تشير إلى سمات تملأ تأثيرات render-ui الخاصة بها الصفحة.
تعريف الصفحة (Page)
تُعرَّف الصفحة (Page) في برنامج .orb بالبنية التالية:
{
"name": "TaskListPage",
"path": "/tasks",
"viewType": "list",
"primaryEntity": "Task",
"traits": [
{ "ref": "TaskBrowser", "linkedEntity": "Task" },
{ "ref": "FilterPanel", "linkedEntity": "Task" }
]
}
خصائص الصفحة
| الخاصية | مطلوبة | الوصف |
|---|---|---|
name | نعم | معرِّف بصيغة PascalCase (مثال: TaskListPage) |
path | نعم | مسار URL يبدأ بـ / |
viewType | لا | تلميح دلالي: list، detail، create، edit، dashboard، custom |
primaryEntity | لا | الكيان الأساسي الذي تعمل عليه هذه الصفحة |
traits | نعم | مصفوفة مراجع السمات التي تقود الواجهة |
isInitial | لا | ما إذا كانت هذه صفحة نقطة الدخول |
المسارات وأنماط المسارات
مسارات الصفحات تحدد مسارات URL لتطبيقك.
قواعد المسارات
- يجب أن تبدأ بـ
/ - الأحرف الصالحة: حروف، أرقام، شرطات، شرطات سفلية، نقطتان، شرطات مائلة
- يجب أن تكون فريدة عبر جميع الصفحات في البرنامج
المسارات الثابتة
مسارات بسيطة بدون أجزاء ديناميكية:
{ "path": "/tasks" }
{ "path": "/dashboard" }
{ "path": "/settings/profile" }
الأجزاء الديناميكية
استخدم صيغة النقطتين للمعاملات الديناميكية:
{ "path": "/tasks/:id" }
{ "path": "/users/:userId/tasks/:taskId" }
{ "path": "/projects/:projectId/members/:memberId" }
تُستخرج الأجزاء الديناميكية وتكون متاحة في:
- حمولات الأحداث (
@payload.id) - تأثيرات التنقل
- عمليات البحث عن الكيانات
أمثلة المسارات
| المسار | الوصف |
|---|---|
/tasks | صفحة قائمة المهام |
/tasks/:id | تفاصيل مهمة واحدة |
/tasks/create | إنشاء مهمة جديدة |
/tasks/:id/edit | تعديل مهمة موجودة |
/users/:id/profile | ملف المستخدم |
/dashboard | عرض لوحة المعلومات |
أنواع العرض (View Types)
أنواع العرض هي تلميحات دلالية حول غرض الصفحة:
| النوع | الغرض | الأنماط النموذجية |
|---|---|---|
list | عرض مجموعة كيانات | entity-table، entity-cards، entity-list |
detail | عرض كيان واحد | entity-detail، stats |
create | إنشاء كيان جديد | form |
edit | تعديل كيان موجود | form |
dashboard | نظرة عامة بأقسام متعددة | dashboard-grid، stats |
custom | تخطيط مخصص | أي أنماط |
مهم: أنواع العرض لا تقيّد الواجهة. العرض الفعلي يُتحكم به عبر تأثيرات render-ui في السمات. أنواع العرض هي بيانات وصفية لأجل:
- التوثيق
- تلميحات توليد الكود
- بناء هيكل الواجهة
ربط الصفحة بالسمة
الصفحات تشير إلى السمات التي توفر سلوكها وواجهتها.
مراجع السمات
{
"pages": [
{
"name": "TaskListPage",
"path": "/tasks",
"traits": [
{ "ref": "TaskBrowser", "linkedEntity": "Task" },
{ "ref": "QuickActions", "linkedEntity": "Task", "config": { "showCreate": true } }
]
}
]
}
بنية PageTraitRef
| الخاصية | مطلوبة | الوصف |
|---|---|---|
ref | نعم | اسم السمة أو مسارها (مثال: "TaskBrowser"، "Std.traits.CRUD") |
linkedEntity | لا | الكيان الذي تعمل عليه هذه السمة |
config | لا | إعدادات خاصة بالسمة |
سمات متعددة لكل صفحة
يمكن للصفحة أن تحتوي على سمات متعددة، كل منها تساهم بواجهة في فتحات مختلفة:
{
"name": "DashboardPage",
"path": "/dashboard",
"traits": [
{ "ref": "StatsSummary", "linkedEntity": "Analytics" },
{ "ref": "RecentActivity", "linkedEntity": "Activity" },
{ "ref": "QuickActions", "linkedEntity": "Task" }
]
}
كل تأثير render-ui في السمة يستهدف فتحات محددة.
linkedEntity على السمات
خاصية linkedEntity تربط السمة بكيان محدد:
{ "ref": "StatusManager", "linkedEntity": "Task" }
هذا يعني:
- ربط
@entityفي السمة يُحل إلى بياناتTask - تأثيرات مثل
persistتعمل على مجموعةTask - آلة حالة السمة تدير نسخ
Task
انظر ربط السمة بالكيان للتفاصيل.
الكيان الأساسي (Primary Entity)
خاصية primaryEntity تشير إلى الكيان الرئيسي الذي تعمل عليه الصفحة:
{
"name": "TaskDetailPage",
"path": "/tasks/:id",
"primaryEntity": "Task",
"traits": [
{ "ref": "TaskViewer" },
{ "ref": "CommentList", "linkedEntity": "Comment" }
]
}
الاستخدام:
- الكيان الافتراضي للسمات بدون
linkedEntityصريح - التحقق من وجود الكيان
- تلميحات توليد الكود
- ليس مطلوباً إذا حددت جميع السمات كيانها صراحة
الفتحات وعرض واجهة المستخدم
السمات تعرض واجهة المستخدم عبر تأثيرات render-ui التي تستهدف الفتحات (Slots) - مناطق مسماة في الصفحة.
الفتحات المتاحة
| الفتحة | الغرض |
|---|---|
main | منطقة المحتوى الأساسي |
sidebar | لوحة جانبية |
modal | طبقة نافذة منبثقة |
drawer | لوحة درج |
overlay | طبقة ملء الشاشة |
center | محتوى مركزي |
toast | إشعارات مؤقتة |
hud-top | HUD علوي (واجهة الألعاب) |
hud-bottom | HUD سفلي (واجهة الألعاب) |
floating | عنصر عائم |
system | مكونات نظام غير مرئية |
تأثير render-ui
السمات تملأ الفتحات باستخدام تأثير render-ui:
["render-ui", "main", {
"type": "entity-table",
"entity": "Task",
"columns": ["title", "status", "dueDate"],
"itemActions": [
{ "event": "VIEW", "label": "View" },
{ "event": "EDIT", "label": "Edit" }
]
}]
مسار الفتحات
┌─────────────────────────────────────────────────────────────┐
│ Page: TaskListPage │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Slot: main │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Pattern: entity-table (from TaskBrowser) │ │ │
│ │ │ - Columns: title, status, dueDate │ │ │
│ │ │ - Actions: VIEW, EDIT │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Slot: sidebar │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Pattern: filter-panel (from FilterPanel) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
عمليات عرض متعددة لنفس الفتحة
إذا عرضت سمات متعددة في نفس الفتحة، تتراكم (الأحدث تستبدل أو تُضاف حسب نوع النمط):
// السمة A
["render-ui", "main", { "type": "stats", ... }]
// السمة B (لاحقاً في الصفحة)
["render-ui", "main", { "type": "entity-table", ... }]
التنقل (Navigation)
التنقل بين الصفحات يُعالج عبر تأثير navigate في السمات.
تأثير navigate
["navigate", "/tasks/:id", { "id": "@payload.taskId" }]
الصيغة: ["navigate", path, params?]
| الوسيط | الوصف |
|---|---|
path | مسار الصفحة المستهدفة (يمكن أن يتضمن أجزاء ديناميكية) |
params | كائن اختياري لملء الأجزاء الديناميكية |
أمثلة التنقل
تنقل بسيط:
["navigate", "/dashboard"]
مع معرِّف الكيان:
["navigate", "/tasks/@entity.id"]
مع الحمولة:
["navigate", "/tasks/:id", { "id": "@payload.taskId" }]
مسار متداخل:
["navigate", "/users/:userId/tasks/:taskId", {
"userId": "@entity.assigneeId",
"taskId": "@entity.id"
}]
التنقل في الانتقالات
التنقل عادة يحدث بعد تغييرات الحالة:
{
"from": "editing",
"to": "saved",
"event": "SAVE",
"effects": [
["persist", "update", "Task", "@entity.id", "@payload"],
["notify", "Task saved!", "success"],
["navigate", "/tasks/@entity.id"]
]
}
انظر التأثيرات لمزيد من التفاصيل.
الصفحة الأولية (Initial Page)
حدد صفحة كنقطة دخول باستخدام isInitial:
{
"name": "HomePage",
"path": "/",
"isInitial": true,
"traits": [
{ "ref": "WelcomeBanner" }
]
}
السلوك:
- التطبيق يحمّل هذه الصفحة أولاً
- إعادة التوجيه من الجذر (
/) تذهب إلى هنا - صفحة واحدة فقط يجب تحديدها كأولية لكل وحدة مدارية
التحقق من الصفحة (Page Validation)
تُتحقق الصفحات في وقت التصريف بهذه القواعد:
الحقول المطلوبة
name- يجب أن يكون بصيغة PascalCasepath- يجب أن يبدأ بـ/، أحرف صالحة فقطtraits- يجب أن تحتوي على مرجع سمة واحد على الأقل
أخطاء التحقق
| الخطأ | الوصف |
|---|---|
PageMissingName | اسم الصفحة مطلوب |
PageMissingPath | مسار الصفحة مطلوب |
PageInvalidPath | المسار لا يطابق النمط |
PageEmptyTraits | مصفوفة السمات لا يمكن أن تكون فارغة |
PageInvalidTraitRef | السمة المشار إليها غير موجودة |
PageInvalidViewType | viewType ليس في القائمة الصالحة |
PageDuplicatePath | صفحة أخرى تستخدم نفس المسار |
مثال كامل
مثال صفحة كاملة مع سمات متعددة:
orbital TaskManagement {
entity Task {
id : string!
title : string!
status : string
assigneeId : string
}
trait TaskBrowser -> Task {
initial: idle
state idle {
INIT -> viewing
(fetch Task { })
(render-ui main { type: "entity-table", entity: "Task", fields: ["title", "status", "assigneeId"], columns: ["title", "status", "assigneeId"], itemActions: [{ event: "VIEW", label: "View" }] })
}
state viewing {
VIEW -> viewing
(navigate "/tasks/@payload.id" { id: "@entity.id" })
}
}
trait TaskViewer -> Task {
initial: loading
state loading {
INIT -> viewing
(fetch Task { id: "@payload.id" })
(render-ui main { type: "detail-panel", entity: "Task", fields: ["title", "status", "assigneeId"] })
}
state viewing {
EDIT -> viewing
(navigate "/tasks/@entity.id/edit")
BACK -> viewing
(navigate "/tasks" { id: "@entity.id" })
}
}
page "/tasks" -> TaskBrowser
page "/tasks/:id" -> TaskViewer
}
المبادئ الأساسية
-
الصفحات مدفوعة بالسمات - الصفحات حاويات لمراجع السمات. الواجهة تنبثق من تأثيرات
render-uiفي السمات، وليس من تعريفات الصفحات. -
بنية الفتحات - الواجهة تتدفق عبر فتحات موحدة (
main،sidebar،modal)، مما يمكّن تركيب التخطيطات بدون تعليمات ثابتة. -
المسار كعقد - مسار الصفحة هو الواجهة الأساسية، يحدد عنوان URL الذي يتنقل إليه المستخدمون.
-
ربط الكيان الصريح -
linkedEntityعلى مراجع السمات يجعل علاقات الكيانات صريحة. -
لا حالة للصفحة - الصفحات تركيبية بحتة. كل الحالة تعيش في آلات حالة السمات.
-
التنقل مدفوع بالتأثيرات - التنقل هو تأثير يُحفَّز بانتقالات السمات، وليس خاصية صفحة.
ملخص
يوفر نظام الصفحات في Orb:
- التوجيه - تنقل مبني على المسارات مع أجزاء ديناميكية
- تركيب السمات - سمات متعددة لكل صفحة، كل منها تساهم بواجهة
- الفتحات - مناطق مسماة لوضع الواجهة (main، sidebar، modal، إلخ.)
- أنواع العرض - تلميحات دلالية لغرض الصفحة (list، detail، dashboard)
- التنقل - توجيه مدفوع بالتأثيرات بين الصفحات
- ربط الكيان - علاقات كيان صريحة عبر
linkedEntity - التحقق - المُصرِّف يفرض تفرد المسارات ووجود السمات
الصفحات هي طبقة التوجيه والتركيب - تحدد أين يذهب المستخدمون، بينما السمات تحدد ماذا يحدث والكيانات تحدد ما هي البيانات المتضمنة.