4 Commits

Author SHA1 Message Date
78c5fd1fd4 release: floret-kit 0.1.0
Finalize the 0.1.0 CHANGELOG entry (first tagged release). Consumed from
source via git submodule; tagging gives apps a stable, traceable pin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:58:54 +02:00
e1919cab83 components: FullScreenPicker takes an optional actions slot
Forwards a trailing app-bar `actions` lambda through to CollapsingScaffold,
so a full-screen picker can carry a commit/extra action — a custom-value
Add/OK, a Reset — instead of being select-and-dismiss only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 14:23:07 +02:00
55ad536513 identity: add Modifier.animateContentSizeMotion()
Eases a composable's own height/size changes on the family's M3 Expressive
spatial motion scheme (fastSpatialSpec) instead of jumping — e.g. a text
field that wraps to a new line, or a value swapped for a longer one. Snaps
under reduced motion, like the other identity motion helpers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:55:57 +02:00
cded44207c core-time: add LocalDate.isoWeekNumber() (ISO-8601 week-of-year)
Centralises the calendar-week-number computation — ISO-8601
week-of-week-based-year (Monday-start, week 1 holds the first Thursday) —
that Calendula previously inlined identically in its Week and Month grid
headers. Names the scheme choice (there are several incompatible "week
number" definitions) and covers the year-boundary rollover cases with a
unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:14:20 +02:00
5 changed files with 87 additions and 3 deletions

View File

@@ -10,6 +10,8 @@ are no binary releases; "version" tracks the shared `version` in the root build
## [Unreleased]
## [0.1.0] - 2026-07-07
### Added
- **`core-time`** (JVM library) — pure-Kotlin date/time helpers, no Android and
@@ -18,6 +20,8 @@ are no binary releases; "version" tracks the shared `version` in the root build
- `Instant.formatDate()` / `formatTime()` / `formatDateTime()` — locale- and
zone-aware display formatting over `kotlin.time.Instant`.
- `TimeBridge``Long` epoch-millis ↔ `Instant`, for content-provider I/O.
- `LocalDate.isoWeekNumber()` — ISO-8601 week-of-week-based-year, the
calendar-week number for week/month grids (one scheme, shared).
- **`core-crash`** (Android library) — privacy-respecting, on-device crash
capture and reporting. Writes an allowlist-only report (app/Android/device
version, locale, time, stack trace — nothing else) to private storage and
@@ -28,8 +32,11 @@ are no binary releases; "version" tracks the shared `version` in the root build
own thin themed `CrashReportActivity`.
- **`identity`** (Android library) — the family's Material 3 Expressive theme
factory `FloretExpressiveTheme(lightScheme, darkScheme, …)` (dynamic colour on
API 31+, system light/dark fallback, the standard motion scheme) plus
`rememberNavSlideSpec()`. Each app passes its own seed-derived colour schemes
API 31+, system light/dark fallback, the standard motion scheme) plus the
shared motion helpers — `rememberNavSlideSpec()`, the content transitions,
`Modifier.predictiveBack()` and `Modifier.animateContentSizeMotion()` (eases a
block's own height changes, e.g. a text field wrapping to a new line), each
reduced-motion-aware. Each app passes its own seed-derived colour schemes
and typography — the mechanics are shared, the look is not.
- **`components`** (Android library) — the shared Compose component vocabulary:
- `GroupedSurface` / `GroupedRow` + `Position` / `positionOf` — the canonical
@@ -41,7 +48,9 @@ are no binary releases; "version" tracks the shared `version` in the root build
`largeTopBar = false` for a pinned single-line bar (the picker variant).
- `FullScreenPicker` / `OptionPicker` — the full-screen single-select picker,
now over a pinned single-line bar (a picker is a short list, so no tall
collapsing header to scroll past).
collapsing header to scroll past). `FullScreenPicker` takes an optional
`actions` slot (app-bar trailing items) for pickers that carry a commit or
extra action — a custom-value **Add**/**OK**, a **Reset**.
- `ReorderableColumn` — dependency-free drag-to-reorder for the short, fixed
grouped-card Settings lists; pairs with `GroupedRow(gapBelow = false)`, which
hands the column uniform control of row spacing.

View File

@@ -2,6 +2,7 @@ package de.jeanlucmakiola.floret.components
import android.view.WindowManager
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material3.Icon
@@ -25,6 +26,7 @@ fun FullScreenPicker(
title: String,
onDismiss: () -> Unit,
predictiveBack: Boolean = false,
actions: @Composable RowScope.() -> Unit = {},
content: @Composable ColumnScope.() -> Unit,
) {
Dialog(
@@ -48,6 +50,7 @@ fun FullScreenPicker(
onBack = onDismiss,
largeTopBar = false,
predictiveBack = predictiveBack,
actions = actions,
content = content,
)
}

View File

@@ -0,0 +1,16 @@
package de.jeanlucmakiola.floret.time
import java.time.LocalDate
import java.time.temporal.IsoFields
/**
* ISO-8601 week-of-week-based-year (1..53) for [this] date: weeks start on
* Monday and week 1 is the one containing the year's first Thursday. This is the
* calendar-week number shown in week/month grids.
*
* Centralised here so every surface agrees on one scheme — "week number" has
* several incompatible definitions (ISO vs. locale-first-day vs. US), and the
* ISO year a date belongs to can differ from its calendar year around New Year
* (e.g. 2018-12-31 is week 1 of 2019; 2021-01-01 is week 53 of 2020).
*/
fun LocalDate.isoWeekNumber(): Int = get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)

View File

@@ -0,0 +1,37 @@
package de.jeanlucmakiola.floret.time
import com.google.common.truth.Truth.assertThat
import org.junit.jupiter.api.Test
import java.time.LocalDate
class IsoWeekTest {
@Test
fun `first Thursday's week is week 1`() {
// 2026-01-01 is a Thursday → ISO week 1.
assertThat(LocalDate.of(2026, 1, 1).isoWeekNumber()).isEqualTo(1)
}
@Test
fun `mid-year week`() {
assertThat(LocalDate.of(2026, 6, 15).isoWeekNumber()).isEqualTo(25)
}
@Test
fun `late-December days can roll into week 1 of the next ISO year`() {
// 2018-12-31 (Mon) belongs to ISO week 1 of 2019.
assertThat(LocalDate.of(2018, 12, 31).isoWeekNumber()).isEqualTo(1)
}
@Test
fun `early-January days can belong to the last week of the previous ISO year`() {
// 2021-01-01 (Fri) belongs to ISO week 53 of 2020.
assertThat(LocalDate.of(2021, 1, 1).isoWeekNumber()).isEqualTo(53)
}
@Test
fun `a 53-week year reports week 53`() {
// 2020-12-31 (Thu) is ISO week 53.
assertThat(LocalDate.of(2020, 12, 31).isoWeekNumber()).isEqualTo(53)
}
}

View File

@@ -3,9 +3,12 @@ package de.jeanlucmakiola.floret.identity
import android.provider.Settings
import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -14,6 +17,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import kotlin.coroutines.cancellation.CancellationException
@@ -36,6 +40,21 @@ fun rememberReduceMotion(): Boolean {
}
}
/**
* Animates a composable's own size changes with the family's spatial motion, so
* a block that grows or shrinks — a text field wrapping to a new line, a value
* swapped for a longer one — eases into its new height instead of jumping. Wraps
* Compose's [androidx.compose.animation.animateContentSize] onto the active M3
* Expressive motion scheme; under reduced motion the new size snaps in.
*
* Apply it high in the modifier chain of the element whose height changes.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun Modifier.animateContentSizeMotion(reduceMotion: Boolean = rememberReduceMotion()): Modifier =
if (reduceMotion) this
else animateContentSize(animationSpec = MaterialTheme.motionScheme.fastSpatialSpec<IntSize>())
/**
* The standard Android predictive-back transform for a full-screen surface: as
* the back gesture is dragged, the surface scales toward ~90%, shifts toward the