diff --git a/CHANGELOG.md b/CHANGELOG.md index bfcda89..27dc94b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.9.0 + +### πŸ› Fixes + +- **`display:flex; flex-wrap:wrap` with flex children crashed the frame** ([#15](https://github.com/brewkits/hyper_render/issues/15)): a wrapping flex container was mapped to Flutter's `Wrap`, but its items were still wrapped in `FlexItemWidget`, which emits `Expanded`/`Flexible`. `Wrap` provides `WrapParentData`, so Flutter threw *"Incorrect use of ParentDataWidget … wants to apply ParentData of type FlexParentData"* and cascaded into `RenderBox was not laid out` / `child.hasSize is not true`. +- **Horizontal wrapping flex is now a real render object, `RenderFlexWrap`.** Neither Flutter built-in can express CSS here: `Wrap` has no `flex-grow` and rejects flex parent data, while a `LayoutBuilder`-driven `Column` of `Row`s cannot answer intrinsic or dry-layout queries β€” and CSS's default `align-items: stretch` puts an `IntrinsicHeight` above every nested flex container, so *nested* wrapping flex crashed with `LayoutBuilder does not support returning intrinsic dimensions` plus ~34 cascading layout errors. `RenderFlexWrap` packs lines, distributes free space by `flex-grow`, implements intrinsics, dry layout, painting and hit-testing, and adds no `IntrinsicHeight` at all. `flex-direction: column` + wrap still uses `Wrap` (its main axis is height, which is unbounded), with all flex parent data stripped. +- **`flex-basis`, `min-width` and `max-width` were parsed but never applied to flex items.** `flex: 0 0 50%` and a bare `min-width: 300px` both collapsed the item to its content width (~32px in a 500px container) instead of 250px / 300px. All three now drive wrapping-flex sizing, including their percentage forms β€” `min-width` accepts `%` for the first time, and the `flex: 1` / `flex: 1 1` shorthands now correctly imply CSS's `0%` basis rather than `auto`. +- **CSS `align-items: baseline` asserted on every flex container**: `CrossAxisAlignment.baseline` was handed to `Row`/`Column` without a `textBaseline`. Both now pass `TextBaseline.alphabetic`. +- **An ` `-only flex item disappeared entirely**: `_buildFlexChild` used `String.trim().isEmpty` to detect insignificant whitespace, but Dart follows Unicode (U+00A0 is whitespace) while CSS Text Level 3 does not. It now uses `isCssWhitespaceOnly`, and trims only CSS whitespace so a leading/trailing ` ` survives. + +### πŸ“ Documentation + +- `CSS_PROPERTIES_MATRIX.md`: `align-content` corrected from βœ… to ❌ for **both** flex and grid β€” it is parsed into `ComputedStyle` and read by nothing in the render path. `test/docs_matrix_sync_test.dart` now guards it against regressing. +- `flex-basis` documented as ⚠️: it drives wrapping (row) containers only; the `nowrap` path still sizes from content. + ## 1.8.0 - **AI & LLM Real-Time Token Streaming Engine**: diff --git a/README.md b/README.md index 0425230..3656b2e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Flutter](https://img.shields.io/badge/Flutter-3.10+-54C5F8.svg?logo=flutter)](https://flutter.dev) -**CSS float Β· crash-free selection Β· AI/LLM streaming Β· CJK/Furigana Β· `@keyframes` Β· 2 460+ tests Β· XSS-safe Β· Zero Gradle config** +**CSS float Β· crash-free selection Β· AI/LLM streaming Β· CJK/Furigana Β· `@keyframes` Β· 2 490+ tests Β· XSS-safe Β· Zero Gradle config**
@@ -34,7 +34,7 @@ Already using `flutter_html`? You don't need to rewrite your widget tree or lear ```dart // 1. In your pubspec.yaml: // dependencies: -// hyper_render: ^1.8.0 +// hyper_render: ^1.9.0 // 2. In your Dart file β€” replace this single line: // ❌ import 'package:flutter_html/flutter_html.dart'; @@ -68,7 +68,7 @@ Html( ```yaml dependencies: - hyper_render: ^1.8.0 + hyper_render: ^1.9.0 ``` ```dart @@ -127,7 +127,7 @@ HyperRender renders the whole document inside **one custom `RenderObject`**. CSS | RTL / BiDi (Arabic, Hebrew) | ⚠️ | ⚠️ | βœ… | | CSS Variables `var()` | ❌ | ❌ | βœ… | | CSS `@keyframes` animation | ❌ | ❌ | βœ… | -| Flexbox / Grid | ⚠️ Partial | ⚠️ Partial | βœ… Full | +| Flexbox / Grid | ⚠️ Partial | ⚠️ Partial | βœ… Wrapping flex on a custom RenderObjectΒΉ | | `box-shadow` Β· `filter` | ❌ | ❌ | βœ… | | `list-style-type` (all 11 values) | ⚠️ disc only | ⚠️ disc only | βœ… | | `
` / `` | ❌ | ❌ | βœ… Interactive | @@ -136,6 +136,8 @@ HyperRender renders the whole document inside **one custom `RenderObject`**. CSS | Modular packages | ❌ monolith | ❌ monolith | βœ… opt-in add-ons | | Zero Gradle config | βœ… | βœ… | βœ… | +ΒΉ `flex-wrap: wrap` on a row container performs real CSS line packing and distributes free space by `flex-grow`, honouring `flex-basis` / `min-width` / `max-width` including their `%` forms. Known gaps: `align-content` is not applied, `flex-basis` does not drive the `nowrap` path, and `flex-direction: column` + wrap packs lines without growth. Per-property status: [CSS_PROPERTIES_MATRIX.md](doc/CSS_PROPERTIES_MATRIX.md). + ### Benchmarks Measured on iPhone 13 + Pixel 6 with a 25 000-character article: @@ -194,6 +196,22 @@ Ruby copied to clipboard as `東京(とうきょう)`. ### CSS Variables Β· Flexbox Β· Grid +Responsive wrapping cards β€” `flex-wrap: wrap` packs items into lines and shares +each line's free space by `flex-grow`, so the same markup a browser gets works +here: + +```dart +HyperViewer(html: ''' +
+
Card 1
+
Card 2
+
Card 3
+
+''') +``` + +CSS custom properties and grid: + ```dart HyperViewer(html: '''