Lompat ke konten
dokumentasi.id
Commit 120a248
v1
Star0

Ada commit baru di branch utama.

0792dcf

7

Komponen UI Pelajaran

Dua jalur tampilan pelajaran memakai komponen yang berbeda. Latihan pemula dirender oleh PracticeSheet, pertemuan lanjutan dirender oleh MeetingLesson yang menyusun setiap langkah sebagai tab, dan satu halaman mushaf di keduanya selalu dirender oleh MushafPageView. Semua jalur ini berada di dalam FullscreenFrame yang dipasang sekali di layout, sehingga berpindah halaman tidak mematikan mode layar penuh (src/app/pemula/layout.tsx:1-5, src/app/lanjutan/[slug]/layout.tsx:1-5).

Komposisi komponen

FullscreenFrame menyimpan keadaan layar penuh dan membagikannya lewat FullscreenContext, dengan useFullscreen sebagai pembacanya (src/components/fullscreen-frame.tsx:7-11). Karena itu PracticeSheet, MeetingTabs, dan FullscreenToggle dapat menyesuaikan tampilannya tanpa saling menerima props.

graph TD
  Frame["FullscreenFrame"] --> Sheet["PracticeSheet"]
  Frame --> Lesson["MeetingLesson"]
  Frame --> Toggle["FullscreenToggle"]
  Sheet --> Index["drillIndex"]
  Sheet --> Overall["OverallProgress"]
  Lesson --> Tabs["MeetingTabs"]
  Tabs --> Body["StepBody"]
  Body --> Materi["Materi"]
  Body --> Drills["Drills"]
  Body --> Practice["MushafPractice"]
  Practice --> Picker["MushafPicker"]
  Picker --> Page["MushafPageView"]
  Page --> Opening["SurahOpening"]
  Page --> Marker["AyahMarker"]
  style Frame fill:#f59e0b,color:#000
  style Toggle fill:#f59e0b,color:#000
  style Sheet fill:#2d7d46,color:#fff
  style Lesson fill:#2d7d46,color:#fff
  style Index fill:#8b5cf6,color:#fff
  style Overall fill:#8b5cf6,color:#fff
  style Page fill:#8b5cf6,color:#fff
  style Marker fill:#8b5cf6,color:#fff
  style Opening fill:#8b5cf6,color:#fff
%% fig: Komposisi komponen tampilan pelajaran
%% desc: Satu FullscreenFrame membungkus PracticeSheet dan MeetingLesson; MeetingLesson menurunkan tab, langkah, dan halaman mushaf beserta penanda ayat.
Fig. 1: Komposisi komponen tampilan pelajaranSatu FullscreenFrame membungkus PracticeSheet dan MeetingLesson; MeetingLesson menurunkan tab, langkah, dan halaman mushaf beserta penanda ayat.

MeetingLesson hanya penyusun tab. Setiap MeetingStep diubah menjadi satu MeetingTab oleh infoOf, lalu isinya dipilih StepBody menurut step.kind (src/components/lesson/meeting-lesson.tsx:239-261). Langkah mushaf tidak merender MushafPageView secara langsung, melainkan lewat MushafPicker yang menampilkan satu halaman pada satu waktu dan menyediakan baris tombol nomor halaman bila halamannya lebih dari satu (src/components/lesson/mushaf-picker.tsx:6-11).

step.kindid tabLabel
materimateriMateri
drillslatihan-ayatLatihan ayat
mushafpraktik-mushafPraktik mushaf
tasktugas-mandiriTugas mandiri
otherlangkah-<nomor>step.title atau Catatan

Pemetaan di atas berasal dari stepInfo dan infoOf (src/components/lesson/meeting-lesson.tsx:14-24).

PracticeSheet: satu latihan sebagai lembar drill

PropTipeDipakai untuk
slugstringkunci progres dan tautan pelajaran terakhir
numbernumbernomor latihan, juga dipakai practicePart
titlestringjudul pada header
guidestring[]isi panel Petunjuk
sectionsSheetSection[]label, sound key, dan baris drill
previous, nextNeighbournavigasi di layar penuh dan ajakan lanjut
totalsRecord<string, number>OverallProgress di layar penuh
partCurrentPartbagian kurikulum yang sedang dibuka

Seluruh prop tersebut dideklarasikan pada tipe Props (src/components/practice/practice-sheet.tsx:69-80). Tiap section membawa label, key berisi pasangan bacaan dan bentuk huruf, serta rows berupa kisi teks Arab (src/components/practice/practice-sheet.tsx:26-31).

PracticeSheet hanya menghitung section yang punya rows sebagai drill, lalu menomornya berurutan supaya tanda lancar tidak bergeser ketika label diubah (src/components/practice/practice-sheet.tsx:99-103). Saat latihan dibuka, visitPractice menyimpannya sebagai yang terakhir dipelajari (src/components/practice/practice-sheet.tsx:109-112); cara progres disimpan di perangkat dibahas di Progres Belajar di Perangkat.

Label yang berbentuk sound key dirender berbeda dari label biasa. Barisnya memakai dir="rtl" dan forms dibalik, sehingga entri fathah berada di tepi kanan dan dhammah di tepi kiri (src/components/practice/practice-sheet.tsx:213-241). Baris drill dirender sebagai paragraf dir="rtl" berukuran text-[1.75rem] yang membesar di layar penuh, sedangkan pairs membungkus grup latihan dua per dua agar baris empat grup pecah 2 + 2 (src/components/practice/practice-sheet.tsx:258-271, src/components/practice/practice-sheet.tsx:43-48).

Tombol penanda hanya muncul pada section yang masuk drillIndex. aria-pressed mengikuti isDone dan teksnya berganti dari Tandai lancar menjadi Sudah lancar (src/components/practice/practice-sheet.tsx:289-307). Setelah semua bagian ditandai, footer menampilkan ajakan lanjut ke latihan berikutnya dan tombol resetPractice (src/components/practice/practice-sheet.tsx:312-345). Tombol layar penuh di header ditulis sendiri oleh PracticeSheet, bukan memakai FullscreenToggle, dan memilih ikon Maximize2 atau Minimize2 menurut fullscreen.active (src/components/practice/practice-sheet.tsx:167-179).

MeetingLesson dan MeetingTabs

MeetingTabs menyimpan tab aktif sebagai state lokal active dan merender semua panel sekaligus, dengan hidden pada panel yang tidak aktif (src/components/lesson/meeting-tabs.tsx:20-53). Saat tab diganti, open mengembalikan posisi ke atas kontainer bila pembaca sudah melewati tablist (src/components/lesson/meeting-tabs.tsx:14-18). Di layar penuh, FullscreenToggle muncul di ujung baris tab karena header halaman disembunyikan (src/components/lesson/meeting-tabs.tsx:44-45).

Langkah berjenis other yang datang berurutan digabung ke tab terakhir, jadi catatan seperti form ujian berbagi satu tab di bawah judulnya sendiri (src/components/lesson/meeting-lesson.tsx:271-294).

Teks materi tidak dirender sebagai satu paragraf. flow memecahnya menjadi potongan Latin dan potongan Arab (src/components/lesson/meeting-lesson.tsx:36-47), lalu ArabicLine mengubah huruf tunggal menjadi kotak size-14 dan kata utuh menjadi teks font-arabic text-3xl (src/components/lesson/meeting-lesson.tsx:57-64). Praktik mushaf di dalam pertemuan menyetel --mushaf-size ke 1.75rem, lebih kecil dari ukuran pembaca mushaf, supaya halaman pas di samping materi (src/components/lesson/meeting-lesson.tsx:220-237).

MushafPageView: satu halaman mushaf

MushafPageView merender satu halaman cetak: bingkai luar, baris juz dan nama surah, ayat per baris, lalu nomor halaman dalam angka Arab di bawah (src/components/mushaf/mushaf-page-view.tsx:44-56). Nama surah pada baris atas diambil dari surahNames, yang mengelompokkan ayat halaman lewat groupBySurah (src/components/mushaf/mushaf-page-view.tsx:7).

Garis bawah tiap baris dibuat dari background yang berulang setiap 2.25em, sehingga garis mengikuti ukuran baca --mushaf-size dan bukan jumlah baris (src/components/mushaf/mushaf-page-view.tsx:58-74). Setiap ayat diberi AyahMarker berupa bintang dengan nomor ayat, dan surah baru dibuka oleh SurahOpening yang menyertakan basmalah bila showsBismillah benar (src/components/mushaf/mushaf-page-view.tsx:9-41). Asal data halaman dan rentang juz dijelaskan di Data Qur'an dan Mushaf.

Keadaan layar penuh

FullscreenFrame memisahkan dua jalur: native yang mengikuti document.fullscreenElement dan overlay untuk peramban tanpa element fullscreen, sedangkan active adalah gabungan keduanya (src/components/fullscreen-frame.tsx:18-22). Yang diminta masuk fullscreen adalah div pembungkus milik frame itu sendiri, dan saat aktif div tersebut menerima atribut data-fullview (src/components/fullscreen-frame.tsx:79-89).

stateDiagram-v2
  [*] --> normal
  normal --> native: requestFullscreen berhasil
  normal --> overlay: requestFullscreen ditolak
  native --> normal: exitFullscreen
  overlay --> normal: tombol Keluar
  overlay --> normal: tombol Escape
  style normal fill:#2d7d46,color:#fff
  style native fill:#8b5cf6,color:#fff
  style overlay fill:#f59e0b,color:#000
%% fig: Perpindahan keadaan layar penuh
%% desc: Frame masuk mode native bila peramban mengizinkan, atau jatuh ke overlay yang ditutup tombol Keluar maupun Escape.
Fig. 2: Perpindahan keadaan layar penuhFrame masuk mode native bila peramban mengizinkan, atau jatuh ke overlay yang ditutup tombol Keluar maupun Escape.

Varian fullview: didefinisikan sebagai @custom-variant fullview (&:where([data-fullview] *)), jadi semua kelas fullview: hanya berlaku untuk turunan frame yang sedang aktif (src/app/globals.css:6-7). FullscreenToggle adalah tombol siap pakai untuk frame ini: labelnya berganti antara Layar penuh dan Keluar mengikuti active (src/components/fullscreen-frame.tsx:93-104).

ElemenKelas atau perilakuEfek di layar penuhRujukan
artikel PracticeSheetfullview:border-0 fullview:bg-transparent fullview:shadow-none fullview:max-w-[110rem]kartu dilepas dan lembar melebarsrc/components/practice/practice-sheet.tsx:115-116
header PracticeSheetfullview:rounded-none fullview:bg-papersudut header tidak lagi membulatsrc/components/practice/practice-sheet.tsx:115-116
OverallProgress di headerdirender hanya bila fullscreen.activeprogres keseluruhan bagian ikut tampilsrc/components/practice/practice-sheet.tsx:138-142
tautan previous dan nextdirender hanya bila fullscreen.activenavigasi tetap terjangkausrc/components/practice/practice-sheet.tsx:147-166
kontainer MeetingTabsfullview:mt-0jarak atas dihapussrc/components/lesson/meeting-lesson.tsx:296-308
header halaman lanjutanfullview:hiddenjudul dan tombol di atas materi disembunyikansrc/app/lanjutan/[slug]/page.tsx:146
footer dan tautan tetanggafullview:hiddenchrome situs disembunyikansrc/app/lanjutan/[slug]/page.tsx:173

⚠️ Bila requestFullscreen tidak tersedia, misalnya di iPhone Safari, toggle memasang overlay fixed inset-0 z-[70] overflow-y-auto. Overlay tidak punya chrome peramban untuk keluar, jadi Escape menutupnya dan document.body.style.overflow dipulihkan saat overlay dilepas (src/components/fullscreen-frame.tsx:31-64).