Skip to content
Draft
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
5 changes: 4 additions & 1 deletion resources/android/ListItemRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil3.compose.SubcomposeAsyncImage
Expand All @@ -49,6 +50,7 @@ object ListItemRenderer {

// Colors
val headlineColor = p.getColor("headline_color", 0)
val headlineLineThrough = p.getBool("headline_line_through")
val supportingColor = p.getColor("supporting_color", 0)
val overlineColor = p.getColor("overline_color", 0)
val containerColor = p.getColor("container_color", 0)
Expand Down Expand Up @@ -117,7 +119,8 @@ object ListItemRenderer {
Text(
text = headline,
fontFamily = nuiDefaultFontFamily(),
color = if (headlineColor != 0) Color(headlineColor) else Color.Unspecified
color = if (headlineColor != 0) Color(headlineColor) else Color.Unspecified,
textDecoration = if (headlineLineThrough) TextDecoration.LineThrough else null
)
},
modifier = clickModifier,
Expand Down
4 changes: 3 additions & 1 deletion resources/ios/NativeUIListItemRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct NativeUIListItemRenderer: View {

// Colors
let headlineColor = p.getColor("headline_color", default: 0)
let headlineLineThrough = p.getBool("headline_line_through")
let supportingColor = p.getColor("supporting_color", default: 0)
let overlineColor = p.getColor("overline_color", default: 0)
let containerColor = p.getColor("container_color", default: 0)
Expand Down Expand Up @@ -79,7 +80,8 @@ struct NativeUIListItemRenderer: View {
.nuiScaledFont(size: 12)
.foregroundColor(overlineColor != 0 ? Color(argb: overlineColor) : .secondary)
}
Text(headline)
// Text-level strikethrough (iOS 13+), same as NativeUITextRenderer.
(headlineLineThrough ? Text(headline).strikethrough() : Text(headline))
.nuiScaledFont(size: 17)
.foregroundColor(headlineColor != 0 ? Color(argb: headlineColor) : .primary)
if !supporting.isEmpty {
Expand Down
16 changes: 16 additions & 0 deletions src/Elements/ListItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ public function applyAttributes(array $attrs): void
if (isset($attrs['headlineColor'])) {
$this->headlineColor($attrs['headlineColor']);
}
// Struck-through headline, e.g. a completed todo. Same name as the
// `line-through` text decoration on `<native:text>`.
$lineThrough = $attrs['headline-line-through'] ?? $attrs['headlineLineThrough'] ?? null;
if ($lineThrough !== null) {
$this->headlineLineThrough(filter_var($lineThrough, FILTER_VALIDATE_BOOLEAN));
}
if (isset($attrs['supportingColor'])) {
$this->supportingColor($attrs['supportingColor']);
}
Expand Down Expand Up @@ -470,6 +476,16 @@ public function headlineColor(string $color): static
return $this;
}

/**
* Draw a line through the headline, e.g. to mark a todo as done.
*/
public function headlineLineThrough(bool $lineThrough = true): static
{
$this->listItemProps['headline_line_through'] = $lineThrough;

return $this;
}

public function supportingColor(string $color): static
{
$this->listItemProps['supporting_color'] = $this->resolveColorValue($color);
Expand Down
49 changes: 49 additions & 0 deletions tests/ListItemLineThroughTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

use Native\Mobile\Edge\CallbackRegistry;
use Native\Mobile\Edge\ElementRegistry;
use Native\Mobile\Edge\NativeElementCollector;
use Native\Mobile\UI\Elements\ListItem;

beforeEach(function () {
NativeElementCollector::reset();
ElementRegistry::reset();
ElementRegistry::register('list_item', ListItem::class);
});

afterEach(function () {
NativeElementCollector::reset();
ElementRegistry::reset();
});

function lineThroughProps(array $attrs): array
{
NativeElementCollector::leaf('list_item', ['headline' => 'Buy milk'] + $attrs);

return NativeElementCollector::collect()->toArray(new CallbackRegistry)['props'] ?? [];
}

it('leaves the headline undecorated by default', function () {
expect(lineThroughProps([]))->not->toHaveKey('headline_line_through');
});

it('strikes through the headline from either Blade spelling', function (string $attr) {
expect(lineThroughProps([$attr => true])['headline_line_through'])->toBeTrue();
})->with(['headline-line-through', 'headlineLineThrough']);

it('follows a bound value so done and not-done rows share one tag', function (mixed $value, bool $expected) {
expect(lineThroughProps(['headline-line-through' => $value])['headline_line_through'])->toBe($expected);
})->with([
[true, true],
[false, false],
['true', true],
['false', false],
[1, true],
[0, false],
]);

it('exposes a fluent builder', function () {
$props = ListItem::make('Buy milk')->headlineLineThrough()->toArray(new CallbackRegistry)['props'];

expect($props['headline_line_through'])->toBeTrue();
});
Loading