Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ body:
options:
- ui-tiles (core components)
- ui-tiles-extended (advanced components)
- ui-base (internal/shared)
- ui-tiles-expressive (expressive Material 3)
- ui-core (foundation/shared)
- New module
- Not sure
validations:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT_VERSION"

build-lib-base:
name: Build base - Main
build-lib-core:
name: Build core - Main
if: ${{ success() }}
needs: [ get-version ]
runs-on: macos-latest
Expand All @@ -50,7 +50,7 @@ jobs:
java-version: 17

- name: Build
run: ./gradlew :ui-base:build
run: ./gradlew :ui-core:build

build-lib-ui:
strategy:
Expand All @@ -62,7 +62,7 @@ jobs:
]
name: Build ${{ matrix.config.target }} - Main
if: ${{ success() }}
needs: [ build-lib-base ]
needs: [ build-lib-core ]
runs-on: macos-latest
steps:
- name: Check out code
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
env:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }}
run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication
run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication

fake-publish:
name: Fake publish - Main
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/publish-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ jobs:
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version to publish: $CURRENT_VERSION"

build-lib-base:
name: Build base - Release
build-lib-core:
name: Build core - Release
needs: [ get-version ]
runs-on: macos-latest
steps:
Expand All @@ -69,7 +69,7 @@ jobs:
java-version: 17

- name: Build
run: ./gradlew :ui-base:build
run: ./gradlew :ui-core:build

build-lib-ui:
strategy:
Expand All @@ -80,7 +80,7 @@ jobs:
{ target: tiles expressive, module: ui-tiles-expressive, task: build, continueOnError: false },
]
name: Build ${{ matrix.config.target }} - Release
needs: [ build-lib-base ]
needs: [ build-lib-core ]
runs-on: macos-latest
steps:
- name: Check out code
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
env:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }}
run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication
run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication

fake-publish:
name: Fake publish - Release
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ jobs:
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT_VERSION"

build-lib-base:
name: Build base - PR
build-lib-core:
name: Build core - PR
if: ${{ success() }}
needs: [ get-version ]
runs-on: macos-latest
Expand All @@ -51,7 +51,7 @@ jobs:
java-version: 17

- name: Build
run: ./gradlew :ui-base:build
run: ./gradlew :ui-core:build

build-lib-ui:
strategy:
Expand All @@ -63,7 +63,7 @@ jobs:
]
name: Build ${{ matrix.config.target }} - PR
if: ${{ success() }}
needs: [ build-lib-base ]
needs: [ build-lib-core ]
runs-on: macos-latest
steps:
- name: Check out code
Expand Down Expand Up @@ -125,7 +125,7 @@ jobs:
env:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }}
run: ./gradlew ui-base:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication
run: ./gradlew ui-core:signKotlinMultiplatformPublication ui-tiles:signKotlinMultiplatformPublication ui-tiles-extended:signKotlinMultiplatformPublication ui-tiles-expressive:signKotlinMultiplatformPublication

fake-publish:
name: Fake publish - PR
Expand Down
68 changes: 52 additions & 16 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,30 @@ Compose-Settings is a Kotlin Multiplatform library that provides pre-built setti

```
ui-tiles ──────────┐
├──> ui-base (internal)
ui-tiles-extended ─┘
ui-tiles-extended ─┼──> ui-core (foundation)
ui-tiles-expressive┘
```

### Module Responsibilities

#### ui-base
#### ui-core

**Purpose**: Internal foundation module providing shared infrastructure
**Purpose**: Foundation module providing shared infrastructure

**Location**: `ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/`
**Location**: `ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/`

**Key Components**:
- `SettingsTileScaffold` - Base composable for all settings components
- `SettingsTileDefaults` - Default values for colors, shapes, text styles
- `SettingsTileCoreDefaults` - Shared default values for colors and text styles
- `SettingsTileDefaults` - Module-specific defaults (e.g., shapes)
- `SettingsTileColors` - Color system for settings components
- `SettingsTextStyles` - Typography system
- `LocalSettingsGroupEnabled` - Composition local for hierarchical enabled state
- `SettingsTileConstants` - Shared constants

**Visibility**: All APIs are `internal` - not exposed to library consumers
**Visibility**: Most APIs are `internal` - not exposed to library consumers

**Why separate module?**
- Prevents duplication between `ui-tiles` and `ui-tiles-extended`
Expand Down Expand Up @@ -78,6 +82,7 @@ ui-tiles-extended ─┘

**Components**:
- `SettingsSlider` - Numeric value selection with slider
- `SettingsSegmented` - Single-choice selection using Material 3 `SingleChoiceSegmentedButtonRow`

**Artifact**: `com.github.alorma.compose-settings:ui-tiles-extended`

Expand All @@ -86,6 +91,36 @@ ui-tiles-extended ─┘
- Advanced components may have different Material 3 requirements
- Users can opt-in to extended features as needed

#### ui-tiles-expressive

**Purpose**: Expressive Material 3 variants of all standard tiles, plus expressive-only components

**Location**: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/`

**Components**:
- `SettingsMenuLink` - Expressive variant built on `SegmentedListItem`
- `SettingsCheckbox` - Expressive variant built on `SegmentedListItem`
- `SettingsTriStateCheckbox` - Expressive variant built on `SegmentedListItem`
- `SettingsRadioButton` - Expressive variant built on `SegmentedListItem`
- `SettingsSwitch` - Expressive variant built on `SegmentedListItem`
- `SettingsGroup` - Expressive variant (same API as M3 version)
- `SettingsButtonGroup` - Button group using `OutlinedToggleButton` (expressive-only)

All components expose a `shapes: ListItemShapes` parameter (instead of `shape: Shape`) enabling
the connected/grouped visual treatment via `ListItemDefaults.segmentedShapes(index, count)`.

**Requires**: `@OptIn(ExperimentalMaterial3ExpressiveApi::class)`

**Artifact**: `com.github.alorma.compose-settings:ui-tiles-expressive`

**Why mirror the standard tiles?**
- Consumers can build a full settings screen using only the expressive module, without
also depending on `ui-tiles`
- The `shapes` parameter allows precise control of corner rounding so groups of items
connect visually as a single segmented unit
- `SegmentedListItem` provides the interactive checked state and segmented appearance
out of the box, which standard `ListItem` does not

## Design Patterns

### Scaffold Pattern
Expand Down Expand Up @@ -245,7 +280,7 @@ fun CustomSettingsComponent(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: SettingsTileColors = SettingsTileDefaults.colors(),
colors: ListItemColors = SettingsTileDefaults.colors(),
) {
val groupEnabled = LocalSettingsGroupEnabled.current
val actualEnabled = enabled && groupEnabled
Expand Down Expand Up @@ -356,19 +391,20 @@ All dependencies in `gradle/libs.versions.toml`:

## Design Decisions

### Why Internal ui-base Module?
### Why ui-core Foundation Module?

**Problem**: Both `ui-tiles` and `ui-tiles-extended` need shared components
**Problem**: All component modules (`ui-tiles`, `ui-tiles-extended`, `ui-tiles-expressive`) need shared components

**Options considered**:
1. Duplicate code in both modules ❌
2. Make one depend on the other ❌ (circular dependency potential)
3. Create internal shared module ✅
1. Duplicate code in all modules ❌
2. Make modules depend on each other ❌ (circular dependency potential)
3. Create shared foundation module ✅

**Decision**: Internal `ui-base` module
**Decision**: `ui-core` foundation module
- Prevents duplication
- Both modules depend on it
- Not exposed to consumers (implementation detail)
- All component modules depend on it
- Contains scaffolds, colors, styles, and shared utilities
- Published as a separate artifact for flexibility

### Why Composable Content Parameters?

Expand Down
77 changes: 57 additions & 20 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,76 @@ Jetpack Compose. It targets Android, iOS, Desktop (JVM), JavaScript, and WebAsse

### Module Structure

The project is organized into three main library modules:

1. **ui-base** - Foundation module containing shared internal components
- Location: `ui-base/src/commonMain/kotlin/com/alorma/compose/settings/ui/base/internal/`
- Provides: `SettingsTileScaffold`, `SettingsTileDefaults`, `SettingsTileColors`,
`CompositionLocals`
- Contains no public API - only internal utilities used by other modules

2. **ui-tiles** - Core settings components
The project is organized into four main library modules:

1. **ui-core** - Foundation module with shared logic and Material 3 scaffold
- Location: `ui-core/src/commonMain/kotlin/com/alorma/compose/settings/ui/core/`
- Provides: `SettingsTileColors`, `SettingsTextStyles`, `SettingsTileCoreDefaults`,
`CompositionLocals` (`LocalSettingsGroupEnabled`, `LocalSettingsTileColors`,
`LocalSettingsTextStyles`), `SettingsTileConstants`, `SettingsTileScaffold` (wraps Material
3's `ListItem`)
- Contains internal utilities and data models used by all other modules
- Depends on: standard Material 3
- Published as: `com.github.alorma.compose-settings:ui-core`

2. **ui-tiles** - Core settings components (standard Material 3)
- Location: `ui-tiles/src/commonMain/kotlin/com/alorma/compose/settings/ui/`
- Depends on: `ui-base`
- Depends on: `ui-core`
- Components: `SettingsMenuLink`, `SettingsCheckbox`, `SettingsRadioButton`, `SettingsSwitch`,
`SettingsTriStateCheckbox`, `SettingsGroup`
- Published as: `com.github.alorma.compose-settings:ui-tiles`

3. **ui-tiles-extended** - Advanced/extended settings components
3. **ui-tiles-extended** - Advanced/extended settings components (standard Material 3)
- Location: `ui-tiles-extended/src/commonMain/kotlin/com/alorma/compose/settings/ui/`
- Depends on: `ui-base`
- Depends on: `ui-core`
- Components: `SettingsSlider`, `SettingsSegmented`
- Published as: `com.github.alorma.compose-settings:ui-tiles-extended`

4. **ui-tiles-expressive** - Expressive Material 3 components (full tile set + expressive-only)
- Location: `ui-tiles-expressive/src/commonMain/kotlin/com/alorma/compose/settings/ui/expressive/`
- Depends on: `ui-core`, Material 3 Expressive
- Components: `SettingsMenuLink`, `SettingsCheckbox`, `SettingsTriStateCheckbox`,
`SettingsRadioButton`, `SettingsSwitch`, `SettingsGroup`, `SettingsButtonGroup`
- All components (except `SettingsGroup` and `SettingsButtonGroup`) are built on
`SegmentedListItem` and accept a `shapes: ListItemShapes` parameter instead of `shape: Shape`
- Requires `@OptIn(ExperimentalMaterial3ExpressiveApi::class)`
- Published as: `com.github.alorma.compose-settings:ui-tiles-expressive`

### Design Patterns

All settings components follow a consistent pattern:
Settings components follow one of two patterns depending on the module:

1. Built on top of `SettingsTileScaffold` (from `ui-base`)
2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors`
3. Use Material 3 components internally (ListItem, Checkbox, Switch, etc.)
4. Support `LocalSettingsGroupEnabled` for hierarchical enabled state
5. Leverage `SettingsTileColors` for consistent theming across all components
**Standard M3 pattern** (`ui-tiles`, `ui-tiles-extended`):
1. Built on top of `SettingsTileScaffold` (from `ui-core`)
2. Accept common parameters: `title`, `subtitle`, `icon`, `modifier`, `enabled`, `colors`, `shape`
3. Use Material 3 `ListItem` internally

**Expressive M3 pattern** (`ui-tiles-expressive`):
1. Built directly on Material 3 Expressive `SegmentedListItem`
2. Accept `shapes: ListItemShapes` instead of `shape: Shape`, enabling segmented list styling
3. Use `ListItemDefaults.segmentedShapes(index, count)` to connect items into groups

Both patterns:
- Support `LocalSettingsGroupEnabled` for hierarchical enabled state
- Leverage `SettingsTileColors` (from `ui-core`) for consistent theming

The `SettingsTileScaffold` wraps Material 3's `ListItem` and provides consistent layout and color
handling.
handling (standard modules only).

### Architecture Layers

The library uses a layered architecture:

```
ui-core (foundation - colors, styles, scaffold, composition locals)
ui-tiles, ui-tiles-extended, ui-tiles-expressive (components)
```

This separation allows:
- **Code reuse**: Shared foundation (colors, styles, scaffold) used by all component modules
- **Flexibility**: Easy to add new component modules
- **Type safety**: Strong separation between foundation layer and component layer

### Platform-Specific Code

Expand Down Expand Up @@ -166,7 +203,7 @@ The default hierarchy template is applied via `applyDefaultHierarchyTemplate()`.
The project uses Gradle convention plugins located in `build-logic/convention/`:

1. **ComposeLibraryConventionPlugin** (`compose.library`)
- Applied to all library modules (ui-base, ui-tiles, ui-tiles-extended)
- Applied to all library modules (ui-core, ui-tiles, ui-tiles-extended, ui-tiles-expressive)
- Configures Kotlin Multiplatform with all target platforms
- Sets up Compose Multiplatform and Compose Compiler
- Configures Android library settings
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ By participating in this project, you agree to abide by our [Code of Conduct](CO

```
Compose-Settings/
├── ui-base/ # Foundation module (internal components)
├── ui-core/ # Foundation module (shared components)
├── ui-tiles/ # Core settings components
├── ui-tiles-extended/ # Advanced settings components
├── ui-tiles-expressive/ # Expressive Material 3 components
├── samples/ # Sample applications
│ ├── androidApp/ # Android demo
│ ├── desktopApp/ # Desktop (JVM) demo
Expand Down Expand Up @@ -151,7 +152,7 @@ When creating new settings components:
- `ui-tiles-extended`: Advanced components requiring additional dependencies (slider, etc.)

2. **Use the scaffold pattern**:
- Build on `SettingsTileScaffold` from `ui-base`
- Build on `SettingsTileScaffold` from `ui-core`
- This ensures consistent layout and theming

3. **Follow the API pattern**:
Expand All @@ -163,7 +164,7 @@ When creating new settings components:
subtitle: @Composable (() -> Unit)? = null,
icon: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
colors: SettingsTileColors = SettingsTileDefaults.colors(),
colors: ListItemColors = SettingsTileDefaults.colors(),
// Component-specific parameters
) {
// Implementation using SettingsTileScaffold
Expand Down
Loading