Pantrix UI

Sidebar

Bir uygulamanın yanındaki, uygulamanın bölümlerini de tutan hedefler sütunu.

import com.pantrixui.components.navigation.sidebar.PantrixSidebar

Örnekler

Gruplar ve bölümler

En yalın hâli: gruplar hâlinde satırlar (groups), bulunulan hedefin anahtarı (selected), açık bölümlerin anahtarları (expanded) ve ikisinin değişmesini isteyen iki geri bildirim. Sidebar kendiliğinden bir şey değiştirmez; durum burada rememberSaveable ile tutulur ve ekran dönünce kaybolmaz.

"Kalite" ve "Sürümler" birer bölümdür: basılınca yerinde açılır, biri açılınca öteki kapanır. "Çökmeler"i seç ve "Kalite"yi kapat: bölümün adı kalın kalır, kapalıyken de nerede olunduğu belli olur. "Arşiv" ve "Faturalar" basılamaz.

adaptive = false verildi: sidebar burada bir kutunun içinde durur ve varsayılan hâliyle raya inerdi; bunu "Kendiliğinden daralır" örneği gösterir. Kutunun boyu 480dp: sidebar bulunduğu yerin boyu kadardır.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 480.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        tagKey = "plain"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Birden çok bölüm açık kalabilir

singleExpanded = false: bir bölümü açmak ötekini kapatmaz. "Sürümler"i aç: "Kalite" de açık kalır.

Sidebar, satırları kutusuna sığmadığında onları kendi içinde kaydırır: telefonda iki bölüm birden açılınca 480dp'lik kutuda böyle olur.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 480.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        singleExpanded = false,
        tagKey = "many"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Adı olan gruplar

Bir grubun adı satırlarının üstünde durur: SidebarGroup(items, title = "Rehberler"). Ad küçük, büyük harflerle ve sessiz renkte yazılır, ekran okuyucu onu bir başlık olarak duyar: bir belgelemenin "Başlarken" ve "Rehberler" bölümleri gibi. Adı olmayan bir grup yalnızca bir çizgiyle ayrılır.

Rayda adlara yer yoktur: "Raya çevir"e bas, gruplar yine bir çizgiyle ayrılır.

var rail by rememberSaveable { mutableStateOf(false) }
var where by rememberSaveable { mutableStateOf("installation") }
PantrixButton(
    text = if (rail) "Sütuna çevir" else "Raya çevir",
    onClick = { rail = !rail },
    variant = ButtonVariant.Outline,
    size = ButtonSize.Small,
    tagKey = "titled_shape"
)
SidebarFrame(height = 400.dp) {
    PantrixSidebar(
        groups = TITLED,
        selected = where,
        onSelect = { where = it },
        expanded = emptySet(),
        onExpandedChange = {},
        collapsed = rail,
        adaptive = false,
        tagKey = "titled"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Basılamayan satırlar

enabled = false satırı temanın devre dışı yazı rengiyle yazar; satır basış almaz. "Faturalar" basılamayan bir hedeftir.

"Raporlar" basılamayan bir bölümdür: açılıp kapanmaz, expanded ne diyorsa öyle kalır; burada açık. İçindeki hedeflerin her biri kendi enabled'ına bakar: "Haftalık" basılır, "Aylık" basılmaz.

var where by rememberSaveable { mutableStateOf("overview") }
SidebarFrame(height = 320.dp) {
    PantrixSidebar(
        groups = listOf(
            SidebarGroup(
                SidebarItem(key = "overview", label = "Genel bakış", icon = PantrixIcons.Star),
                SidebarItem(key = "invoices", label = "Faturalar", icon = PantrixIcons.Person, enabled = false),
                SidebarItem(
                    key = "reports",
                    label = "Raporlar",
                    icon = PantrixIcons.Calendar,
                    children = listOf(
                        SidebarItem(key = "weekly", label = "Haftalık"),
                        SidebarItem(key = "monthly", label = "Aylık", enabled = false)
                    ),
                    enabled = false
                )
            )
        ),
        selected = where,
        onSelect = { where = it },
        expanded = setOf("reports"),
        onExpandedChange = {},
        adaptive = false,
        tagKey = "disabled"
    )
    PantrixVerticalDivider()
    PantrixText(
        text = "Seçili hedefin anahtarı: $where",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

Başlık, sabit hedefler ve alt bilgi

header satırların üstünde durur ve kaymaz: uygulamanın işareti, bir çalışma alanı seçici. footerItems satırların altına sabitlenen hedeflerdir: ayarlar, yardım. Bölüm olamazlar: çocuğu olan biri reddedilir. footer en altta durur: bir hesap, bir plan. Her biri bir çizgiyle ayrılır; aradaki satırlar kalan yeri alır.

Başlık ve alt bilgi çağıranındır. İçlerindeki yazı, kendi rengini söylemedikçe, sidebar'ın yazı rengindedir. İkisi de SidebarSlotScope içinde çizilir ve sidebar'ın hangi hâlde olduğunu collapsed ile öğrenir: burada sütunda ad, rayda yalnızca baş harfler. Rayı bir sonraki örnek gösterir.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 740.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        header = { WorkspaceHeader() },
        footerItems = PINNED,
        footer = { AccountFooter() },
        tagKey = "main"
    )
    OpenPage(where)
}

@Composable
private fun SidebarSlotScope.WorkspaceHeader() {
    if (collapsed) {
        RailLetters("A")
    } else {
        Column(Modifier.padding(PantrixTheme.spacing.lg)) {
            PantrixText(text = "Acme", style = PantrixTheme.typography.title)
            PantrixText(text = "Üretim ortamı", style = PantrixTheme.typography.caption)
        }
    }
}

@Composable
private fun SidebarSlotScope.AccountFooter() {
    if (collapsed) {
        RailLetters("AL")
    } else {
        Column(Modifier.padding(PantrixTheme.spacing.lg)) {
            PantrixText(text = "Ada Lovelace", style = PantrixTheme.typography.label)
            PantrixText(
                text = "ada@acme.example",
                style = PantrixTheme.typography.caption,
                color = PantrixTheme.colors.textSecondary
            )
        }
    }
}

@Composable
private fun ColumnScope.RailLetters(text: String) {
    PantrixText(
        text = text,
        modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = PantrixTheme.spacing.lg),
        style = PantrixTheme.typography.label
    )
}

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

İkon rayı: bölüm yanında bir kart açar

collapsed sidebar'ı ikonlardan bir raya indirir. Bir hedefin adını ikonu taşır: üzerinde bekleyen bir imleç, klavye odağı ya da uzun basış adını bir tooltip'te gösterir. Bir bölüm içindekileri rayın yanında açar; SidebarFlyout.Popup ile bu bir karttır. İmleç bölümün üzerinde durunca gelir, imleç gidince gider; imleç yokken, parmakla, klavyeyle ya da ekran okuyucuyla basınca açılır ve kapatılana kadar kalır. İçinden bir hedef seçilince kapanır.

Bir satırın detail'i ("87 açık") yalnızca burada, yer olan yerde yazılır; bölümünki ("3 uyarı") kartın başında, adının yanındadır. Sütunda yazılmaz: sütun gidilecek yerlerin listesidir, yanından aşağı inen rakamlar veri gibi okunurdu.

"Sütuna çevir" aynı sidebar'ı sütuna çevirir: biçim hemen değişir, genişlik temanın hareketiyle onu izler ve yanındaki sayfa onunla birlikte kayar. Başlık ve alt bilgi rayda yalnızca bir iki harf çizer.

var rail by rememberSaveable { mutableStateOf(true) }
var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
PantrixButton(
    text = if (rail) "Sütuna çevir" else "Raya çevir",
    onClick = { rail = !rail },
    variant = ButtonVariant.Outline,
    size = ButtonSize.Small,
    tagKey = "sidebar_shape"
)
SidebarFrame(height = 740.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        collapsed = rail,
        adaptive = false,
        flyout = SidebarFlyout.Popup,
        header = { WorkspaceHeader() },
        footerItems = PINNED,
        footer = { AccountFooter() },
        tagKey = "rail"
    )
    OpenPage(where)
}

@Composable
private fun SidebarSlotScope.WorkspaceHeader() {
    if (collapsed) {
        RailLetters("A")
    } else {
        Column(Modifier.padding(PantrixTheme.spacing.lg)) {
            PantrixText(text = "Acme", style = PantrixTheme.typography.title)
            PantrixText(text = "Üretim ortamı", style = PantrixTheme.typography.caption)
        }
    }
}

@Composable
private fun SidebarSlotScope.AccountFooter() {
    if (collapsed) {
        RailLetters("AL")
    } else {
        Column(Modifier.padding(PantrixTheme.spacing.lg)) {
            PantrixText(text = "Ada Lovelace", style = PantrixTheme.typography.label)
            PantrixText(
                text = "ada@acme.example",
                style = PantrixTheme.typography.caption,
                color = PantrixTheme.colors.textSecondary
            )
        }
    }
}

@Composable
private fun ColumnScope.RailLetters(text: String) {
    PantrixText(
        text = text,
        modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = PantrixTheme.spacing.lg),
        style = PantrixTheme.typography.label
    )
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

İkon rayı: bölüm yanında bir sütun açar

SidebarFlyout.Panel ile bölümün hedefleri rayın yanında, kendi sütununda açılır: sütunun genişliğinde ve rayın boyunda. Sidebar o kadar genişler; sayfa örtülmez, yana itilir.

"Kalite"ye bas. Sütun bir basışla açılır, imleç geçerken açılmaz. Bölüme yeniden basmak, bir hedef seçmek ve Escape onu kapatır; başka bir bölüme basmak onunkini gösterir. Klavyeyle açıldığında odak sütunun ilk hedefine gider, kapanınca bölümün satırına döner.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 480.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        collapsed = true,
        adaptive = false,
        flyout = SidebarFlyout.Panel,
        tagKey = "panel"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Kendiliğinden daralır: dar yerde ray, geniş yerde sütun

adaptive, ki varsayılanı true'dur, sidebar'ı sütunu sayfayı sıkıştıracağı yerde kendiliğinden raya indirir: kendisine verilen genişlikten sütunun 248dp'si çıkınca geriye 840dp kalmıyorsa. 840dp, altında bir sayfada iki bölmeye yer olmayan genişliktir. Yani sidebar'a 1088dp'den az yer düşüyorsa ray, düşmüyorsa sütun görünür.

Seçici, sidebar'ın durduğu kutunun genişliğini değiştirir: 1200dp'lik kutuda sütun, 700dp'lik kutuda ray görünür. Sidebar pencereye değil kendisine verilen yere bakar: pencere aynı kalır, yalnızca kutu daralır ve sidebar raya iner. Kutu sayfaya sığmadığında yana kayar ve altında bir kaydırma çubuğu çıkar.

Uygulamanın kendi yan menüsü de adaptive bir PantrixSidebar'dır: pencereyi daralt, raya iner; 600dp'nin altında yerini alt kenardaki bar alır.

Öteki örneklerde adaptive = false: biçimi yalnızca collapsed belirler. Bir çekmecenin içindeki sidebar için de doğrusu budur: çekmece sayfayı örter, sıkıştırmaz. adaptive bir sidebar kendisine verilen yeri ölçer; boyutunu IntrinsicSize ile soran bir üst öğenin altında kapatılmalıdır.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
var room by rememberSaveable { mutableStateOf(1200) }
PantrixSegmentButton(
    options = ROOMS,
    selected = room,
    onSelect = { room = it },
    fullWidth = false,
    tagKey = "room"
)
HorizontalScrollFrame(tagKey = "adaptive") {
    SidebarFrame(height = 480.dp, width = room.dp) {
        PantrixSidebar(
            groups = GROUPS,
            selected = where,
            onSelect = { where = it },
            expanded = open.toSet(),
            onExpandedChange = { open = it.toList() },
            adaptive = true,
            tagKey = "adaptive"
        )
        OpenPage(where)
    }
}

private val ROOMS = listOf(
    SegmentOption(value = 1200, label = "Geniş yer · 1200dp", tagKey = "wide"),
    SegmentOption(value = 700, label = "Dar yer · 700dp", tagKey = "narrow")
)

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Seçili satır bir rolün renginde

Yalnızca seçili satırın dolgusu verildi: selectedContainer. Temanın bir rolünün rengi olduğu için üstünde neyin okunduğunu tema bilir: selectedContent verilmeden rolün eşlenik rengi gelir. Verilmeyen her renk temanınki kalır.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 480.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        colors = SidebarColors(selectedContainer = PantrixTheme.colors.success.container),
        tagKey = "role"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Kendi renkleriyle

Uygulamanın kendi rengiyle dolu bir sidebar. Tema bu rengi tanımaz ve üstünde neyin okunduğunu bilemez: dolguyla (container) birlikte yazının (content), seçili satırın dolgusunun ve yazısının rengi de verilir.

Çizgiler verilmedi: dolgunun üstünde yazının renginden bir pay olarak çizilirler. Basılamayan satırlar da o rengin bir payıyla yazılır. Bir rayın açtığı kart ve sütun da aynı renklerle çizilir.

val own = ownColors()
var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 480.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        colors = SidebarColors(
            container = own.color,
            content = own.onColor,
            selectedContainer = own.container,
            selectedContent = own.onContainer
        ),
        tagKey = "own"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Stil: genişlik, satırlar ve köşeler

style sidebar'ın geometrisini değiştirir; yalnızca verilen alanlar değişir, gerisi platformun hazır stilinden gelir. Burada sütun 216dp, satırlar 52dp, bölümün içindeki satırlar 48dp ve satırın dolgusunun köşeleri tam yuvarlak.

Hazır stilde sütun her platformda 248dp'dir; satırlar Android'de ve iOS'ta 44dp, masaüstünde 36dp, web'de 40dp. Bir satır hiçbir zaman platformun dokunma hedefinden alçak olmaz. Renkler stilin parçası değildir: onları tema ve SidebarColors verir.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
SidebarFrame(height = 540.dp) {
    PantrixSidebar(
        groups = GROUPS,
        selected = where,
        onSelect = { where = it },
        expanded = open.toSet(),
        onExpandedChange = { open = it.toList() },
        adaptive = false,
        style = SidebarStyle(width = 216.dp, rowHeight = 52.dp, childRowHeight = 48.dp, shape = ShapeRole.Full),
        tagKey = "styled"
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Kendi yazısıyla

Kendi yazısıyla: satırlar temanın body yazısıyla, yarı kalın (textStyle), grup başlıkları footnote ile, çok kalın (titleTextStyle). Bulunulan hedef verilen yazıdan bir kademe kalın yazılır; yazıların rengi satırındır.

var where by rememberSaveable { mutableStateOf("installation") }
SidebarFrame(height = 360.dp) {
    PantrixSidebar(
        groups = TITLED,
        selected = where,
        onSelect = { where = it },
        expanded = emptySet(),
        onExpandedChange = {},
        adaptive = false,
        tagKey = "own_text",
        textStyle = PantrixTheme.typography.body.copy(fontWeight = FontWeight.SemiBold),
        titleTextStyle = PantrixTheme.typography.footnote.copy(fontWeight = FontWeight.ExtraBold)
    )
    OpenPage(where)
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

Sistem çubuklarının arkasında

insets sidebar'ın kenarlarında boş bıraktığı paydır: dolgu payın arkasına uzanır, başlık ve satırlar payın dışında kalır. Gerçek bir ekranda payı PantrixScaffold yan çubuğuna verir (sideBar = { insets -> … }) ve satırlar sistem çubuklarının dışında durur. Uygulamanın kendi yan menüsü de payını scaffold'dan alır.

Burada scaffold yok: üstteki 24dp'lik şerit sistemin durum çubuğunun yerini tutuyor. Sidebar'ın dolgusu şeridin arkasında sürer, başlığı şeridin altında başlar.

var where by rememberSaveable { mutableStateOf("overview") }
var open by rememberSaveable { mutableStateOf(listOf("quality")) }
Box(modifier = Modifier.clip(PantrixTheme.shapes.md)) {
    SidebarFrame(height = 480.dp) {
        PantrixSidebar(
            groups = GROUPS,
            selected = where,
            onSelect = { where = it },
            expanded = open.toSet(),
            onExpandedChange = { open = it.toList() },
            adaptive = false,
            header = { WorkspaceHeader() },
            insets = PaddingValues(top = 24.dp),
            tagKey = "insets"
        )
        OpenPage(where)
    }
    // Sistemin durum çubuğunun yerinde bir şerit: sidebar'ın dolgusu arkasında sürer.
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(24.dp)
            .background(PantrixTheme.colors.textSecondary.copy(alpha = 0.16f))
    )
}

@Composable
private fun SidebarFrame(
    height: Dp,
    width: Dp = Dp.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    val shape = PantrixTheme.shapes.md
    Row(
        modifier = (if (width.isSpecified) Modifier.width(width) else Modifier.fillMaxWidth())
            .height(height)
            .clip(shape)
            .border(1.dp, PantrixTheme.colors.border, shape),
        content = content
    )
}

@Composable
private fun OpenPage(where: String) {
    PantrixVerticalDivider()
    PantrixText(
        text = "Açık sayfa: ${NAMES.getValue(where)}",
        modifier = Modifier.padding(PantrixTheme.spacing.md),
        color = PantrixTheme.colors.textSecondary
    )
}

@Composable
private fun SidebarSlotScope.WorkspaceHeader() {
    if (collapsed) {
        RailLetters("A")
    } else {
        Column(Modifier.padding(PantrixTheme.spacing.lg)) {
            PantrixText(text = "Acme", style = PantrixTheme.typography.title)
            PantrixText(text = "Üretim ortamı", style = PantrixTheme.typography.caption)
        }
    }
}

private val GROUPS = listOf(
    SidebarGroup(
        SidebarItem("overview", "Genel bakış", PantrixIcons.Star),
        SidebarItem("projects", "Projeler", PantrixIcons.Folder),
        SidebarItem("inbox", "Gelen kutusu", PantrixIcons.Inbox)
    ),
    SidebarGroup(
        SidebarItem(
            key = "quality",
            label = "Kalite",
            icon = PantrixIcons.Search,
            children = listOf(
                SidebarItem("crashes", "Çökmeler", detail = "87 açık"),
                SidebarItem("networks", "Ağ istekleri", detail = "48,2 Mn"),
                SidebarItem("archive", "Arşiv", enabled = false)
            ),
            detail = "3 uyarı"
        ),
        SidebarItem(
            key = "releases",
            label = "Sürümler",
            icon = PantrixIcons.Calendar,
            children = listOf(SidebarItem("planned", "Planlanan"), SidebarItem("shipped", "Yayınlanan"))
        ),
        SidebarItem("billing", "Faturalar", PantrixIcons.Person, enabled = false)
    )
)

@Composable
private fun ColumnScope.RailLetters(text: String) {
    PantrixText(
        text = text,
        modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = PantrixTheme.spacing.lg),
        style = PantrixTheme.typography.label
    )
}

private val NAMES: Map<String, String> = ((GROUPS + TITLED).flatMap { it.items } + PINNED)
    .flatMap { listOf(it) + it.children }
    .associate { it.key to it.label }

private val TITLED = listOf(
    SidebarGroup(
        items = listOf(
            SidebarItem("installation", "Kurulum", PantrixIcons.Download),
            SidebarItem("theming", "Tema", PantrixIcons.Star)
        ),
        title = "Başlarken"
    ),
    SidebarGroup(
        items = listOf(
            SidebarItem("forms", "Formlar", PantrixIcons.File),
            SidebarItem("testing", "Test etmek", PantrixIcons.Check)
        ),
        title = "Rehberler"
    )
)

private val PINNED = listOf(
    SidebarItem("settings", "Ayarlar", PantrixIcons.Menu),
    SidebarItem("help", "Yardım", PantrixIcons.Info)
)

API

groups

Bir uygulamanın yanındaki, uygulamanın bölümlerini de tutan hedefler sütunu.

ParametreTürVarsayılanNe olduğu
groupsList<SidebarGroup>Satırlar, gruplar hâlinde ve sırasıyla. Bir grupla sonraki arasına bir çizgi çizilir.
selectedString?Uygulamanın gösterdiği hedefin anahtarı; hiçbirini göstermiyorsa null.
onSelect(String) -> UnitBasılan hedefin anahtarıyla çağrılır. Sidebar kendiliğinden hiçbir şey değiştirmez.
expandedSet<String>Sütunda açık olan bölümlerin anahtarları. Ray bir bölümü kendi yanında açar ve hangisini açtığını kendisi tutar: uygulamanın bundan geri yükleyeceği bir şey yoktur.
onExpandedChange(Set<String>) -> UnitSütunun bir bölümüne basıldıktan sonra açık olması gereken bölümlerin anahtarlarıyla çağrılır.
modifierModifierModifierSidebar'ın modifier'ı. Sidebar stilinin söylediği kadar geniştir, yanlarındaki insets kadar da genişler. Boyu kendisine verilen yerin boyudur: satırları başlığın, sabit hedeflerin ve alt bilginin bıraktığı yeri alır ve o yerin içinde kayar. Yerin sonu yoksa, yani kayan bir sayfanın içinde, kendi boyu gerekir.
collapsedBooleanfalseSidebar'ın, genişlik ne olursa olsun, ray olarak çizilip çizilmediği. Biçim hemen değişir, sidebar'ın genişliği temanın hareketiyle onu izler ve yanındaki sayfa onunla birlikte kayar.
adaptiveBooleantrueSütunun sayfayı sıkıştıracağı yerde sidebar'ın kendiliğinden raya inip inmediği: kendisine verilen genişlikten sütunun genişliği çıkınca kalan, pencerenin Expanded sınırının (840dp) altındaysa iner. O sınırın altında bir sayfada iki bölmeye yer yoktur. Değeri false ise biçimi yalnızca collapsed belirler: bu, sayfayı örten ve hiçbir şeyi sıkıştırmayan bir çekmecedeki sidebar ya da kullanıcısının sütunu sabitlemesine izin veren bir uygulama içindir. Kendisine verilen yeri ölçer, bu yüzden intrinsic boyutu sorulamaz: boyutu IntrinsicSize ile belirlenen bir üst öğenin altında kapat.
flyoutSidebarFlyoutSidebarFlyout.PopupRayın bir bölümünün ne açtığı: SidebarFlyout.Popup bölümün yanında bir kart, SidebarFlyout.Panel rayın yanında, sayfayı yana iten bir sütun açar. Sidebar sütun hâlindeyken bölümlerini iki durumda da yerinde açar.
singleExpandedBooleantrueSütunun bir bölümünü açmanın, açık olan bölümü kapatıp kapatmadığı.
styleSidebarStyle?nullBu çağrının kendi stili: çevresindeki her stili ezer, yoğunluk ona da uygulanır. Yalnızca verdiği alanlar değişir ve her platformda değişir.
colorsSidebarColors?nullBu sidebar'ın, temanınkilerden farklı olan kendi renkleri: burada verilenler kullanılır, gerisi temanınki kalır. Bir rayın bölümünün açtığı kart ya da sütun da bu renklerle çizilir.
header(@Composable SidebarSlotScope.() -> Unit)?nullSatırların üstünde durur ve kaymaz: uygulamanın işareti, bir çalışma alanı seçici. Bir çizgiyle ayrılır. SidebarSlotScope.collapsed'a bakarak elindeki genişliğe göre yerleşir.
footerItemsList<SidebarItem>emptyList()Satırların altına, bir çizgiden sonra sabitlenen hedefler: ayarlar, yardım. Bölüm olamazlar: çocuğu olan biri reddedilir.
footer(@Composable SidebarSlotScope.() -> Unit)?nullEn altta, footerItems'ın ve bir çizginin altında durur ve kaymaz: bir hesap, bir plan. Başlık gibi, elindeki genişliğe göre yerleşir.
insetsPaddingValuesPaddingValues()Sidebar'ın kenarlarında boş bıraktığı pay. Dolgusu bu payın arkasına uzanır. Bir scaffold'un yan bar'ına verdiği paydır: böylece satırlar sistem çubuklarının dışında durur.
tagKeyString?nullSidebar'ın otomasyon anahtarı: sidebar Sidebar_<tagKey>, içindeki bir hedef nerede gösterilirse gösterilsin Sidebar_<tagKey>.Item_<key>, bir bölüm Sidebar_<tagKey>.Section_<key> olur; bir rayın bölümünün açtığı ise Sidebar_<tagKey>.Flyout_<key> ya da Sidebar_<tagKey>.Panel_<key>. Bir basış Sidebar olayı olarak bildirilir: select hedefin anahtarını ve sidebar'ın o sırada ray olup olmadığını, expand ile collapse bölümün anahtarını taşır.
textStyleTextStylePantrixTheme.typography.labelbir satırın adının yazı stili; sütunda, bir bölümün içinde ve rayın bir bölümünün açtığında: verilmezse temanın label stili. Bulunulan hedef ve onu tutan bölüm ondan bir kademe kalın yazılır. İçindeki bir renk satırın değildir; satırın rengi durumundandır.
titleTextStyleTextStylePantrixTheme.typography.caption.copy(fontWeight = FontWeight.Bold)bir grubun başlığının büyük harflerle yazıldığı stil: verilmezse temanın caption stili, kalın. İçinde hangi renk olursa olsun sidebar'ın sessiz rengiyle yazılır.

SidebarStyle

PantrixSidebar'ın geometrisi: sütun ve ray olarak genişliği, iki düzeydeki satırları ve bir bölümün hedeflerinin onun içinde olduğunu söyleyen çizgi.

ParametreTürVarsayılanNe olduğu
widthDpDp.UnspecifiedSidebar'ın sütun olarak genişliği ve bir rayın bölümünün yanında açtığı sütunun genişliği: 248dp.
collapsedWidthDpDp.UnspecifiedSidebar'ın ikon rayı olarak genişliği: Android'de ve iOS'ta 76dp, masaüstünde 68dp, web'de 72dp.
rowHeightDpDp.UnspecifiedÜst düzey bir satırın, bir hedefin ya da bir bölümün, en az yüksekliği: Android'de ve iOS'ta 44dp, masaüstünde 36dp, web'de 40dp. Bir satır hiçbir zaman platformun dokunma hedefinden alçak olmaz.
childRowHeightDpDp.UnspecifiedBir bölümün içindeki hedefin en az yüksekliği: Android'de ve iOS'ta 40dp, masaüstünde 32dp, web'de 36dp. O da hiçbir zaman platformun dokunma hedefinden alçak olmaz.
shapeShapeSpecShapeSpec.UnspecifiedBir satırın dolgusunun köşeleri: Android'de ve iOS'ta orta, masaüstünde ve web'de küçük.
horizontalPaddingDpDp.UnspecifiedSidebar'ın kenarıyla bir satırın dolgusu arasındaki, bir de dolgunun kenarıyla satırın içindekiler arasındaki boşluk: Android'de ve iOS'ta 12dp, masaüstünde ve web'de 10dp.
iconSizeDpDp.UnspecifiedBir satırın ikonu ve bir bölümün oku: Android'de ve iOS'ta 20dp, masaüstünde ve web'de 18dp.
collapsedIconSizeDpDp.UnspecifiedRayda, satırın ikondan ibaret olduğu yerde, bir satırın ikonu: Android'de ve iOS'ta 24dp, masaüstünde ve web'de 22dp.
iconGapDpDp.UnspecifiedBir satırın ikonuyla etiketinin arası: Android'de ve iOS'ta 12dp, masaüstünde ve web'de 10dp.
childIndentDpDp.UnspecifiedBir bölümün içindeki hedefin, bölümünün başladığı yere göre ne kadar içeriden başladığı: Android'de ve iOS'ta 32dp, masaüstünde ve web'de 28dp.
guideWidthDpDp.UnspecifiedAçık bir bölümün hedefleri boyunca inen çizginin kalınlığı: 1dp.
markerSizeDpDp.UnspecifiedRayın kenarında, uygulamanın bulunduğu satırın yanındaki çubuğun kalınlığı: 4dp.

SidebarColors

Temanın renklerini almayan bir sidebar için PantrixSidebar'ın renkleri: verilen renk kullanılır, verilmeyen temanınki kalır.

ParametreTürVarsayılanNe olduğu
containerColorColor.UnspecifiedSidebar'ın dolgusu. Temanın surface rengi.
contentColorColor.UnspecifiedBir bölümün ikonu ve adı, bir de başlıkla alt bilginin içindekilerden kendi rengini vermeyenler. Temanın textPrimary rengi. Bulunulan hedefin dışındaki hedefler ve bir bölümün oku, temanın kendi dolgusunda temanın textSecondary rengindedir, çağıranın dolgusunda ise bu renkte.
selectedContainerColorColor.UnspecifiedBulunulan hedefin arkasındaki dolgu. primary rolünün container'ı.
selectedContentColorColor.UnspecifiedBulunulan hedefin ikonu ve etiketi: selectedContainer'ın üstünde okunan renk. primary rolünün onContainer'ı.
dividerColorColor.UnspecifiedBaşlığın, grupların, sabit hedeflerin ve alt bilginin arasındaki çizgiler, açık bir bölümün hedefleri boyunca inen çizgi ve bir rayın bölümünün açtığı kartın ya da sütunun kenarı. Temanın kendi zeminlerinde temanın divider rengi. Bir dolgunun üstünde, o çizginin okunmayacağı yerde, content'in bir payı: Subtle bir düğmenin kenarı gibi.
markerColorColor.UnspecifiedRayın kenarında, uygulamanın bulunduğu satırın yanındaki çubuk. Temanın kendi dolgusunda primary rolünün color'ı, çağıranın dolgusunda content.

Erişilebilirlik