Skip to main content

الصفحات (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-topHUD علوي (واجهة الألعاب)
hud-bottomHUD سفلي (واجهة الألعاب)
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 - يجب أن يكون بصيغة PascalCase
  • path - يجب أن يبدأ بـ /، أحرف صالحة فقط
  • traits - يجب أن تحتوي على مرجع سمة واحد على الأقل

أخطاء التحقق

الخطأالوصف
PageMissingNameاسم الصفحة مطلوب
PageMissingPathمسار الصفحة مطلوب
PageInvalidPathالمسار لا يطابق النمط
PageEmptyTraitsمصفوفة السمات لا يمكن أن تكون فارغة
PageInvalidTraitRefالسمة المشار إليها غير موجودة
PageInvalidViewTypeviewType ليس في القائمة الصالحة
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
}

المبادئ الأساسية

  1. الصفحات مدفوعة بالسمات - الصفحات حاويات لمراجع السمات. الواجهة تنبثق من تأثيرات render-ui في السمات، وليس من تعريفات الصفحات.

  2. بنية الفتحات - الواجهة تتدفق عبر فتحات موحدة (main، sidebar، modal)، مما يمكّن تركيب التخطيطات بدون تعليمات ثابتة.

  3. المسار كعقد - مسار الصفحة هو الواجهة الأساسية، يحدد عنوان URL الذي يتنقل إليه المستخدمون.

  4. ربط الكيان الصريح - linkedEntity على مراجع السمات يجعل علاقات الكيانات صريحة.

  5. لا حالة للصفحة - الصفحات تركيبية بحتة. كل الحالة تعيش في آلات حالة السمات.

  6. التنقل مدفوع بالتأثيرات - التنقل هو تأثير يُحفَّز بانتقالات السمات، وليس خاصية صفحة.


ملخص

يوفر نظام الصفحات في Orb:

  1. التوجيه - تنقل مبني على المسارات مع أجزاء ديناميكية
  2. تركيب السمات - سمات متعددة لكل صفحة، كل منها تساهم بواجهة
  3. الفتحات - مناطق مسماة لوضع الواجهة (main، sidebar، modal، إلخ.)
  4. أنواع العرض - تلميحات دلالية لغرض الصفحة (list، detail، dashboard)
  5. التنقل - توجيه مدفوع بالتأثيرات بين الصفحات
  6. ربط الكيان - علاقات كيان صريحة عبر linkedEntity
  7. التحقق - المُصرِّف يفرض تفرد المسارات ووجود السمات

الصفحات هي طبقة التوجيه والتركيب - تحدد أين يذهب المستخدمون، بينما السمات تحدد ماذا يحدث والكيانات تحدد ما هي البيانات المتضمنة.