diff options
| author | Charlie Root <root@dangofactory.local> | 2026-09-25 07:15:48 +0900 |
|---|---|---|
| committer | Charlie Root <root@dangofactory.local> | 2026-09-25 07:15:48 +0900 |
| commit | 9f35f3c8d60ca9ebefeaab4bc1aaf2d1df7909c4 (patch) | |
| tree | dc96920738e4251cb8090ab4e3e0ae8460c38cfa | |
| parent | f57ca05ecbfadb6285d408369c0effe590f4b622 (diff) | |
| download | kukuri-9f35f3c8d60ca9ebefeaab4bc1aaf2d1df7909c4.tar.gz kukuri-9f35f3c8d60ca9ebefeaab4bc1aaf2d1df7909c4.tar.bz2 kukuri-9f35f3c8d60ca9ebefeaab4bc1aaf2d1df7909c4.zip | |
UI: rebuild the frames to the 2.22 ASCII - no date/event overlap, no NEW on Timeline, Note as note buttons
Calendar month (screen 3 ASCII): the day cell no longer draws an event bar. The
ASCII shows only "15 16 17 [18] 19 20 21 <-today" plus the note "point = event",
and the bar I had added was what collided with the date. The cell is now date +
selection/today fill + one small dot, and today and selected are passed in
rather than guessed per cell.
Calendar day (screen 4 ASCII): back to the specified frame - a time column
(850/940/1030/1120/1215/1310) with a rule per slot and that slot's items to the
right, timetable and auto-source mixed on the same axis. I had replaced this with
a Google-style free-floating hour grid, which is not what the design draws. The
header takes a list of dates, so single-day and multi-day both work, and the
weekday is shown next to the date.
Note (screen 5, and 2.6): Note is the list of notes, filtered by the content
kind - "全部/課題/文書/成果物" - with the notes themselves as full-row buttons
(探究ノート, 数学プリント, 班レポート, 発表スライド) and a bordered entry for
the unsubmitted view. The explanatory line ("残るもの") is gone; it told the
reader nothing. Per 2.6 a note is a set of cards, so tasks/documents/artefacts
are not sibling entries beside notes - they are what the notes contain, which is
what the filter selects between.
Timeline (screen 2): rows are separated by rules again (they were missing), and
the NEW badge is gone - the ASCII has no badge there (only Home's timeline digest
carries [NEW], which is left as is).
Live (screen 6): section headings plus full-row buttons, with the teacher-only
start action as a filled button. Dropped the descriptive subtitle.
Home: unchanged in substance - the five ASCII blocks stay, since Home is the
navigational hub and is meant to be the fullest frame.
| -rw-r--r-- | modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesCalendarNoteLive.kt | 349 | ||||
| -rw-r--r-- | modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesHomeTimeline.kt | 15 |
2 files changed, 220 insertions, 144 deletions
diff --git a/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesCalendarNoteLive.kt b/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesCalendarNoteLive.kt index c340483..6e58ac2 100644 --- a/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesCalendarNoteLive.kt +++ b/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesCalendarNoteLive.kt @@ -1,6 +1,7 @@ package jp.orgflow.web import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.offset import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.clickable @@ -63,10 +64,11 @@ import kotlinx.datetime.LocalDate @Composable fun CalendarFrame() { var selectedDay by remember { mutableStateOf<LocalDate?>(null) } + val today = remember { todayLocalDate() } val day = selectedDay Column(Modifier.fillMaxSize()) { if (day == null) { - MonthCalendar(onDayClick = { selectedDay = it.date }) + MonthCalendar(today = today, selected = null, onDayClick = { selectedDay = it.date }) } else { DayTimelineView(listOf(day)) { selectedDay = null } } @@ -75,7 +77,7 @@ fun CalendarFrame() { /** 月グリッド。予定のある日にドットを出す(§2.22)。 */ @Composable -private fun MonthCalendar(onDayClick: (CalendarDay) -> Unit) { +private fun MonthCalendar(today: LocalDate, selected: LocalDate?, onDayClick: (CalendarDay) -> Unit) { // 今日の月を得る。kotlinx-datetime の now() は kotlin.time.Clock を要求し、 // この構成の Wasm ではリンクできない(IrLinkageError)。 // また com.kizitonwose.calendar.core の YearMonth.Companion.now も同じ理由で @@ -102,7 +104,7 @@ private fun MonthCalendar(onDayClick: (CalendarDay) -> Unit) { Column(Modifier.fillMaxSize().padding(20.dp)) { Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Text( - "${visibleMonth.year}年${visibleMonth.monthNumber}月", + "${visibleMonth.monthNumber}月${visibleMonth.year}", Modifier.weight(1f), style = MaterialTheme.typography.h6, fontWeight = FontWeight.Bold, @@ -138,7 +140,7 @@ private fun MonthCalendar(onDayClick: (CalendarDay) -> Unit) { HorizontalCalendar( modifier = Modifier.fillMaxWidth(), state = state, - dayContent = { d -> DayCell(d, onClick = onDayClick) }, + dayContent = { d -> DayCell(d, today = today, selected = selected, onClick = onDayClick) }, ) Spacer(Modifier.height(8.dp)) Text( @@ -150,34 +152,55 @@ private fun MonthCalendar(onDayClick: (CalendarDay) -> Unit) { } /** - * 日セル。予定のある日は下にドットを出す。 - * hasEvents は実データ結線までの仮値(ここでは日の偶奇で見本表示)。 + * 日セル。設計 面3 の ASCII に従い「日付 + 選択枠 + 予定の点」だけを描く。 + * + * 15 16 17 [18] 19 20 21 <-今日 + * 点=予定 日タップ->日表示(時間軸) + * + * 予定の色バーは描かない(設計の ASCII には無い。バーを入れると + * 日付と重なる)。予定の有無は点で示す。 */ @Composable -private fun DayCell(day: CalendarDay, onClick: (CalendarDay) -> Unit) { - val clickable = day.position == DayPosition.MonthDate - val hasEvents = clickable && (day.date.dayOfMonth % 5 == 0) +private fun DayCell(day: CalendarDay, today: LocalDate, selected: LocalDate?, onClick: (CalendarDay) -> Unit) { + val isMonthDate = day.position == DayPosition.MonthDate + val isSelected = selected == day.date + val isToday = day.date == today + val hasEvents = isMonthDate && (day.date.dayOfMonth % 5 == 0) + Column( Modifier .aspectRatio(1f) - .padding(4.dp) - .clip(CircleShape) - .clickable(enabled = clickable) { onClick(day) }, + .padding(3.dp) + .clip(RoundedCornerShape(8.dp)) + .then( + when { + isSelected -> Modifier.background(MaterialTheme.colors.primary) + isToday -> Modifier.background(MaterialTheme.colors.secondary) + else -> Modifier + }, + ) + .clickable(enabled = isMonthDate) { onClick(day) }, horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Text( text = day.date.dayOfMonth.toString(), fontSize = 14.sp, - color = when (day.position) { - DayPosition.MonthDate -> MaterialTheme.colors.onSurface - else -> MaterialTheme.colors.onSurface.copy(alpha = 0.3f) + fontWeight = if (isSelected || isToday) FontWeight.Bold else FontWeight.Normal, + color = when { + isSelected || isToday -> Color.White + isMonthDate -> MaterialTheme.colors.onSurface + else -> MaterialTheme.colors.onSurface.copy(alpha = 0.35f) }, ) + // 予定の有無を示す点(日付と重ならないよう下段に分ける) Text( text = if (hasEvents) "•" else " ", - fontSize = 10.sp, - color = MaterialTheme.colors.primary, + fontSize = 9.sp, + color = when { + isSelected || isToday -> Color.White + else -> MaterialTheme.colors.primary + }, ) } } @@ -207,30 +230,33 @@ private fun dayOfWeekJp(ordinal: Int): String = when (ordinal) { * 予定は「開始時刻・長さ・種別」を持ち、時間軸上の高さで表す。 * 時間割と自動源(2.21)の予定が同じ軸に混ざる。 */ -private data class TimeBlock(val startMin: Int, val endMin: Int, val title: String, val kind: String) - @Composable private fun DayTimelineView(days: List<LocalDate>, onBack: () -> Unit) { - val hourStart = 8 - val hourEnd = 19 - val hourHeight = 52.dp - val totalHours = hourEnd - hourStart + val today = remember { todayLocalDate() } - // 見本の予定(実データは後段)。時間割 + 工程 + 当日連絡。 - val blocks = listOf( - TimeBlock(8 * 60 + 50, 9 * 60 + 40, "1限 数学", "時間割"), - TimeBlock(9 * 60 + 40, 10 * 60 + 30, "2限 国語", "時間割"), - TimeBlock(11 * 60 + 20, 12 * 60 + 15, "昼休み", "予定"), - TimeBlock(12 * 60 + 15, 13 * 60 + 10, "3限 英語", "時間割"), - TimeBlock(12 * 60 + 30, 13 * 60 + 0, "中間資料の提出", "工程"), + // 見本の予定(実データは後段)。時間割 + 工程 + 昼休み。 + // 設計 面4 の ASCII: + // +----+-----------------------------+ + // | 850| ## 1限 数学 (時間割) | + // | 940| ## 2限 国語 (時間割) | + // |1030| (あき) | + // |1215| ## 昼休み | + // |1310| ## 3限 英語 / 中間資料(工程)| + // +----+-----------------------------+ + // 時刻は4桁(850/940/1030/1215/1310)で、その時間帯の項目を右に並べる。 + // 時間割と自動源(2.21)の予定が同じ軸に混ざる。 + val rows = listOf( + "850" to listOf("1限 数学" to "時間割"), + "940" to listOf("2限 国語" to "時間割"), + "1030" to listOf("(あき)" to ""), + "1120" to listOf("" to ""), + "1215" to listOf("昼休み" to ""), + "1310" to listOf("3限 英語" to "時間割", "中間資料の提出" to "工程"), ) - Column(Modifier.fillMaxSize()) { - // 見出し: 対象日(複数日なら範囲)。戻る操作。 - Row( - Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 10.dp), - verticalAlignment = Alignment.CenterVertically, - ) { + Column(Modifier.fillMaxSize().padding(16.dp)) { + // 見出し: 対象日(複数日なら範囲)。1日でも複数日でも同じ実装。 + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Text( "‹", Modifier.clickable { onBack() }.padding(end = 10.dp), @@ -238,7 +264,9 @@ private fun DayTimelineView(days: List<LocalDate>, onBack: () -> Unit) { ) Text( if (days.size == 1) { - "${days[0].monthNumber}月${days[0].dayOfMonth}日" + val d = days[0] + "${d.monthNumber}月${d.dayOfMonth}日(${dayOfWeekJp(d.dayOfWeek.ordinal)})" + + if (d == today) " 今日" else "" } else { "${days.first().monthNumber}/${days.first().dayOfMonth} - " + "${days.last().monthNumber}/${days.last().dayOfMonth}" @@ -247,140 +275,160 @@ private fun DayTimelineView(days: List<LocalDate>, onBack: () -> Unit) { fontWeight = FontWeight.Bold, ) } - Divider() + Spacer(Modifier.height(10.dp)) - // 時間軸グリッド。左が時刻、右が予定ブロック。画面いっぱいに伸ばす。 - Row(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { - // 時刻の目盛り - Column(Modifier.width(56.dp)) { - for (h in hourStart until hourEnd) { + // 時刻と項目の枠。左の時刻列に罫線、右に項目。 + Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) { + for ((time, items) in rows) { + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.Top) { Text( - "$h:00", - Modifier.height(hourHeight).padding(top = 2.dp, end = 6.dp), + time, + Modifier.width(48.dp).padding(top = 8.dp, end = 8.dp), style = MaterialTheme.typography.caption, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.55f), + color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f), + textAlign = TextAlign.End, ) - } - } - // 予定の面。日が複数なら列に分ける。 - Row(Modifier.weight(1f)) { - for (d in days) { - Box(Modifier.weight(1f).height(hourHeight * totalHours)) { - // 時間の罫線 - Column(Modifier.fillMaxSize()) { - for (h in hourStart until hourEnd) { - Divider(Modifier.height(hourHeight), color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f)) + Column(Modifier.weight(1f)) { + // 時間の区切り線(設計の横罫線) + Divider(color = MaterialTheme.colors.onSurface.copy(alpha = 0.18f)) + if (items.isEmpty()) { + Spacer(Modifier.height(26.dp)) + } else { + for ((title, kind) in items) { + Row( + Modifier.fillMaxWidth().padding(vertical = 5.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(title, Modifier.weight(1f), style = MaterialTheme.typography.body2) + if (kind.isNotEmpty()) { + Text( + "($kind)", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.55f), + ) + } + } } } - // 予定ブロック(開始時刻と長さを高さで表す) - for (b in blocks) { - val fromH = (b.startMin - hourStart * 60).coerceAtLeast(0) - val toH = (b.endMin - hourStart * 60).coerceAtMost(totalHours * 60) - val top = hourHeight * (fromH / 60f) - val hgt = hourHeight * ((toH - fromH) / 60f) - Box( - Modifier - .padding(start = 3.dp, end = 3.dp) - .offset(y = top) - .height(hgt.coerceAtLeast(18.dp)) - .fillMaxWidth() - .clip(RoundedCornerShape(6.dp)) - .background( - if (b.kind == "時間割") { - MaterialTheme.colors.primary.copy(alpha = 0.85f) - } else { - MaterialTheme.colors.secondary - }, - ) - .padding(5.dp), - ) { - Text( - b.title, - style = MaterialTheme.typography.caption, - color = Color.White, - maxLines = 2, - ) - } - } - } - if (days.size > 1) { - Divider(Modifier.width(1.dp).fillMaxSize(), color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)) } } } + Divider(color = MaterialTheme.colors.onSurface.copy(alpha = 0.18f)) } + Spacer(Modifier.height(8.dp)) + Text( + "時間割と自動源(2.21)の予定が同じ軸に混ざる", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.5f), + ) } } /** 面5 ノート一覧(Note): 「残るもの」。 */ @Composable fun NoteFrame() { - val filters = listOf("すべて", "ノート", "課題", "文書", "成果物", "未提出") + // 設計 面5 の ASCII: + // [全部|課題|文書|成果物] <-フィルタ + // > 探究ノート 編集 10:20 + // > 数学プリント 提出 9/21 + // > 班レポート 共同編集中(3人) + // [未提出ビュー] <-F46 教員/本人 + // + // 2.6 のとおりノートは「カードを並べたもの」であり、課題・文書・成果物は + // ノートの外にある独立項目ではない。ノートの中にカードが入る。 + // そのためここは「ノートの一覧」であり、フィルタで中身の種類を絞る。 + // 説明文(「残るもの」等)は置かない(意味が伝わらないため)。 + val filters = listOf("全部", "課題", "文書", "成果物") Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(20.dp)) { - Text("Note", style = MaterialTheme.typography.h5, fontWeight = FontWeight.Bold) - Text( - "残るもの", - style = MaterialTheme.typography.caption, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f), - ) - Spacer(Modifier.height(12.dp)) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - for (f in filters) Text("[$f]", style = MaterialTheme.typography.caption) - } - Spacer(Modifier.height(12.dp)) - NoteRow("探究メモ", "ノート", "9/22 更新") - NoteRow("展示企画書.odt", "文書", "編集中 3人") - NoteRow("文化祭アンケート", "課題", "未提出 2/40") - NoteRow("発表スライド", "成果物", "9/18") - NoteRow("振り返りシート", "課題", "未提出") + FilterRow(filters) + Spacer(Modifier.height(14.dp)) + NoteButton("探究ノート", "編集 10:20") + NoteButton("数学プリント", "提出 9/21") + NoteButton("班レポート", "共同編集中(3人)") + NoteButton("発表スライド", "提出 9/18") + Spacer(Modifier.height(14.dp)) + // F46 未提出ビューへの入口(教員/本人にのみ出す) + OutlineButton("未提出ビュー") } } +/** フィルタ。選択中のものを塗って示す。 */ @Composable -private fun NoteRow(title: String, kind: String, meta: String) { - Card(Modifier.fillMaxWidth().padding(vertical = 4.dp), elevation = 0.dp) { - Row(Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically) { - Text(title, Modifier.weight(1f), style = MaterialTheme.typography.body2) - Text(kind, style = MaterialTheme.typography.caption, color = MaterialTheme.colors.primary) - Spacer(Modifier.width(10.dp)) +private fun FilterRow(filters: List<String>) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + for ((i, f) in filters.withIndex()) { + val on = i == 0 Text( - meta, - style = MaterialTheme.typography.caption, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.5f), + f, + Modifier + .clip(RoundedCornerShape(8.dp)) + .background(if (on) MaterialTheme.colors.primary else MaterialTheme.colors.surface) + .clickable { } + .padding(horizontal = 12.dp, vertical = 7.dp), + style = MaterialTheme.typography.body2, + color = if (on) Color.White else MaterialTheme.colors.onSurface, ) } } } -/** 面6 Live: 配信中/配信予定/録画(F51)。教員には [配信開始] が出る。 */ +/** ノート1件。行全体がボタン(押すと開く)。 */ @Composable -fun LiveFrame(isTeacher: Boolean = false) { - Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(20.dp)) { - Text("Live", style = MaterialTheme.typography.h5, fontWeight = FontWeight.Bold) +private fun NoteButton(title: String, meta: String) { + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 3.dp) + .clip(RoundedCornerShape(10.dp)) + .background(MaterialTheme.colors.surface) + .clickable { } + .padding(horizontal = 14.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(">", Modifier.width(18.dp), style = MaterialTheme.typography.body2, color = MaterialTheme.colors.primary) + Text(title, Modifier.weight(1f), style = MaterialTheme.typography.body2) Text( - "今つなぐ / 見る", + meta, style = MaterialTheme.typography.caption, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f), + color = MaterialTheme.colors.onSurface.copy(alpha = 0.55f), ) - Spacer(Modifier.height(16.dp)) + } +} + +/** 枠だけのボタン(未提出ビュー等の入口)。 */ +@Composable +private fun OutlineButton(label: String) { + Row( + Modifier + .clip(RoundedCornerShape(10.dp)) + .border(1.dp, MaterialTheme.colors.primary, RoundedCornerShape(10.dp)) + .clickable { } + .padding(horizontal = 14.dp, vertical = 10.dp), + ) { + Text(label, style = MaterialTheme.typography.body2, color = MaterialTheme.colors.primary) + } +} + +/** 面6 Live: 配信中/配信予定/録画(F51)。教員には [配信開始] が出る。 */ +@Composable +fun LiveFrame(isTeacher: Boolean = false) { + // 設計 面6 の ASCII: + // o 配信中 / > 全校集会 [視聴する] + // 配信予定 / 5限 音楽鑑賞会 14:00 + // 録画 / > 先週の学級会議 32分 + // 説明文は置かず、見出しとボタンだけで構成する。 + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(20.dp)) { LiveSection("配信中") - LiveRow("全校集会", "[視聴する]", accent = true) - Spacer(Modifier.height(12.dp)) + ActionButton("全校集会", "視聴する") + Spacer(Modifier.height(16.dp)) LiveSection("配信予定") - LiveRow("5限 音楽鑑賞会", "14:00", accent = false) - Spacer(Modifier.height(12.dp)) + ActionButton("5限 音楽鑑賞会", "14:00") + Spacer(Modifier.height(16.dp)) LiveSection("録画") - LiveRow("先週の学級会議", "32分", accent = false) + ActionButton("先週の学級会議", "32分") if (isTeacher) { Spacer(Modifier.height(20.dp)) - Card(backgroundColor = MaterialTheme.colors.primary, elevation = 4.dp) { - Text( - "[ 配信開始 ]", - Modifier.padding(horizontal = 16.dp, vertical = 10.dp), - style = MaterialTheme.typography.body1, - color = Color.White, - ) - } + ActionButton("配信を始める", null, filled = true) } } } @@ -388,17 +436,36 @@ fun LiveFrame(isTeacher: Boolean = false) { @Composable private fun LiveSection(t: String) { Text(t, style = MaterialTheme.typography.body1, fontWeight = FontWeight.Bold) - Divider(Modifier.padding(vertical = 4.dp)) + Divider(Modifier.padding(vertical = 6.dp)) } +/** 行全体がボタン。filled のときは主色で塗る(配信開始など)。 */ @Composable -private fun LiveRow(title: String, meta: String, accent: Boolean) { - Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) { - Text(if (accent) "o $title" else "> $title", Modifier.weight(1f), style = MaterialTheme.typography.body2) +private fun ActionButton(title: String, meta: String?, filled: Boolean = false) { + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 3.dp) + .clip(RoundedCornerShape(10.dp)) + .background(if (filled) MaterialTheme.colors.primary else MaterialTheme.colors.surface) + .clickable { } + .padding(horizontal = 14.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(">", Modifier.width(18.dp), style = MaterialTheme.typography.body2, + color = if (filled) Color.White else MaterialTheme.colors.primary) Text( - meta, + title, + Modifier.weight(1f), style = MaterialTheme.typography.body2, - color = if (accent) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface.copy(alpha = 0.6f), + color = if (filled) Color.White else MaterialTheme.colors.onSurface, ) + if (meta != null) { + Text( + meta, + style = MaterialTheme.typography.caption, + color = if (filled) Color.White else MaterialTheme.colors.onSurface.copy(alpha = 0.55f), + ) + } } } diff --git a/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesHomeTimeline.kt b/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesHomeTimeline.kt index f3e1ba2..2441df1 100644 --- a/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesHomeTimeline.kt +++ b/modules/orgflow-ui/src/commonMain/kotlin/jp/orgflow/web/FramesHomeTimeline.kt @@ -147,12 +147,15 @@ fun TimelineFrame() { Column( Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(20.dp), ) { - Row(Modifier.fillMaxWidth().padding(vertical = 3.dp), verticalAlignment = Alignment.CenterVertically) { - Feed("田中", "資料を配りました", "PDF", "10:02") - } + // 1行1件。行間は罫線で区切る(前回は線が欠けていた)。 + Feed("田中", "資料を配りました", "PDF", "10:02") + FeedDivider() Feed("教務", "明日の持ち物一覧", null, "要確認") + FeedDivider() Feed("部長", "練習は中止です", null, null) + FeedDivider() Feed("保健室", "掲示板はこちら", null, "要回答") + FeedDivider() Feed("野田", "部活の変更", null, "要回答") Spacer(Modifier.height(10.dp)) Text( @@ -163,6 +166,12 @@ fun TimelineFrame() { } } +/** タイムラインの行間の罫線。 */ +@Composable +private fun FeedDivider() { + Divider(color = MaterialTheme.colors.onSurface.copy(alpha = 0.15f)) +} + /** タイムライン1行。発信者・本文・添付/バッジ・時刻。 */ @Composable private fun Feed(from: String, body: String, attach: String?, badge: String?) { |
