1+ import 'dart:math' ;
2+
13import 'package:flutter/material.dart' ;
24import 'package:flutter_html/html_parser.dart' ;
35import 'package:flutter_html/src/html_elements.dart' ;
46import 'package:flutter_html/src/styled_element.dart' ;
57import 'package:flutter_html/style.dart' ;
8+ import 'package:flutter_layout_grid/flutter_layout_grid.dart' ;
69import 'package:html/dom.dart' as dom;
710
811/// A [LayoutElement] is an element that breaks the normal Inline flow of
@@ -28,52 +31,108 @@ class TableLayoutElement extends LayoutElement {
2831
2932 @override
3033 Widget toWidget (RenderContext context) {
31- final colWidths = children
32- .where ((c) => c.name == "colgroup" )
33- .map ((group) {
34- return group.children.where ((c) => c.name == "col" ).map ((c) {
35- final widthStr = c.attributes["width" ] ?? "" ;
36- if (widthStr.endsWith ("%" )) {
37- final width =
38- double .tryParse (widthStr.substring (0 , widthStr.length - 1 )) *
39- 0.01 ;
40- return FractionColumnWidth (width);
41- } else {
42- final width = double .tryParse (widthStr);
43- return width != null ? FixedColumnWidth (width) : null ;
44- }
45- });
46- })
47- .expand ((i) => i)
48- .toList ()
49- .asMap ();
34+ final rows = < TableRowLayoutElement > [];
35+ List <TrackSize > columnSizes;
36+ for (var child in children) {
37+ if (child is TableStyleElement ) {
38+ // Map <col> tags to predetermined column track sizes
39+ columnSizes = child.children.where ((c) => c.name == "col" ).map ((c) {
40+ final colWidth = c.attributes["width" ];
41+ if (colWidth != null && colWidth.endsWith ("%" )) {
42+ final percentageSize =
43+ double .tryParse (colWidth.substring (0 , colWidth.length - 1 ));
44+ return percentageSize != null
45+ ? FlexibleTrackSize (percentageSize * 0.01 )
46+ : FlexibleTrackSize (1 );
47+ } else if (colWidth != null ) {
48+ final fixedPxSize = double .tryParse (colWidth);
49+ return fixedPxSize != null
50+ ? FixedTrackSize (fixedPxSize)
51+ : FlexibleTrackSize (1 );
52+ } else {
53+ return FlexibleTrackSize (1 );
54+ }
55+ }).toList (growable: false );
56+ } else if (child is TableSectionLayoutElement ) {
57+ rows.addAll (child.children.whereType ());
58+ } else if (child is TableRowLayoutElement ) {
59+ rows.add (child);
60+ }
61+ }
62+
63+ // All table rows have a height intrinsic to their (spanned) contents
64+ final rowSizes =
65+ List .generate (rows.length, (_) => IntrinsicContentTrackSize ());
66+
67+ // Calculate column bounds
68+ int columnMax = rows
69+ .map ((row) => row.children
70+ .whereType <TableCellElement >()
71+ .fold (0 , (int value, child) => value + child.colspan))
72+ .fold (0 , max);
5073
74+ final cells = < GridPlacement > [];
75+ final columnRowOffset = List .generate (columnMax + 1 , (_) => 0 );
76+ int rowi = 0 ;
77+ for (var row in rows) {
78+ int columni = 0 ;
79+ for (var child in row.children) {
80+ if (columnRowOffset[columni] > 0 ) {
81+ columnRowOffset[columni] = columnRowOffset[columni] - 1 ;
82+ columni++ ;
83+ }
84+ if (child is TableCellElement ) {
85+ cells.add (GridPlacement (
86+ child: Container (
87+ width: double .infinity,
88+ padding: child.style.padding ?? row.style.padding,
89+ decoration: BoxDecoration (
90+ color: child.style.backgroundColor ?? row.style.backgroundColor,
91+ border: child.style.border ?? row.style.border,
92+ ),
93+ child: SizedBox .expand (
94+ child: Container (
95+ alignment: child.style.alignment ?? style.alignment ??
96+ Alignment .centerLeft,
97+ child: StyledText (
98+ textSpan: context.parser.parseTree (context, child),
99+ style: child.style,
100+ ),
101+ ),
102+ ),
103+ ),
104+ columnStart: columni,
105+ columnSpan: child.colspan,
106+ rowStart: rowi,
107+ rowSpan: child.rowspan,
108+ ));
109+ columnRowOffset[columni] = child.rowspan - 1 ;
110+ columni += child.colspan;
111+ }
112+ }
113+ rowi++ ;
114+ }
115+
116+ final finalColumnSizes =
117+ columnSizes ?? List .generate (columnMax, (_) => FlexibleTrackSize (1 ));
51118 return Container (
52- decoration: BoxDecoration (
53- color: style.backgroundColor,
54- border: style.border,
55- ),
56- width: style.width,
57- height: style.height,
58- child: Table (
59- columnWidths: colWidths,
60- children: children
61- .map ((c) {
62- if (c is TableSectionLayoutElement ) {
63- return c.toTableRows (context);
64- }
65- return null ;
66- })
67- .where ((t) {
68- return t != null ;
69- })
70- .toList ()
71- .expand ((i) => i)
72- .toList (),
73- ));
119+ decoration: BoxDecoration (
120+ color: style.backgroundColor,
121+ border: style.border,
122+ ),
123+ width: style.width,
124+ height: style.height,
125+ child: LayoutGrid (
126+ gridFit: GridFit .loose,
127+ templateColumnSizes: finalColumnSizes,
128+ templateRowSizes: rowSizes,
129+ children: cells,
130+ ),
131+ );
74132 }
75133}
76134
135+
77136class TableSectionLayoutElement extends LayoutElement {
78137 TableSectionLayoutElement ({
79138 String name,
@@ -82,19 +141,9 @@ class TableSectionLayoutElement extends LayoutElement {
82141
83142 @override
84143 Widget toWidget (RenderContext context) {
144+ // Not rendered; TableLayoutElement will instead consume its children
85145 return Container (child: Text ("TABLE SECTION" ));
86146 }
87-
88- List <TableRow > toTableRows (RenderContext context) {
89- return children.map ((c) {
90- if (c is TableRowLayoutElement ) {
91- return c.toTableRow (context);
92- }
93- return null ;
94- }).where ((t) {
95- return t != null ;
96- }).toList ();
97- }
98147}
99148
100149class TableRowLayoutElement extends LayoutElement {
@@ -106,35 +155,55 @@ class TableRowLayoutElement extends LayoutElement {
106155
107156 @override
108157 Widget toWidget (RenderContext context) {
158+ // Not rendered; TableLayoutElement will instead consume its children
109159 return Container (child: Text ("TABLE ROW" ));
110160 }
161+ }
162+
163+ class TableCellElement extends StyledElement {
164+ int colspan = 1 ;
165+ int rowspan = 1 ;
111166
112- TableRow toTableRow (RenderContext context) {
113- return TableRow (
114- decoration: BoxDecoration (
115- border: style.border,
116- color: style.backgroundColor,
117- ),
118- children: children
119- .map ((c) {
120- if (c is StyledElement && c.name == 'td' || c.name == 'th' ) {
121- return TableCell (
122- child: Container (
123- padding: c.style.padding,
124- decoration: BoxDecoration (
125- color: c.style.backgroundColor,
126- border: c.style.border,
127- ),
128- child: StyledText (
129- textSpan: context.parser.parseTree (context, c),
130- style: c.style,
131- )));
132- }
133- return null ;
134- })
135- .where ((c) => c != null )
136- .toList ());
167+ TableCellElement ({
168+ String name,
169+ String elementId,
170+ List <String > elementClasses,
171+ @required List <StyledElement > children,
172+ Style style,
173+ dom.Element node,
174+ }) : super (
175+ name: name,
176+ elementId: elementId,
177+ elementClasses: elementClasses,
178+ children: children,
179+ style: style,
180+ node: node) {
181+ colspan = _parseSpan (this , "colspan" );
182+ rowspan = _parseSpan (this , "rowspan" );
183+ }
184+
185+ static int _parseSpan (StyledElement element, String attributeName) {
186+ final spanValue = element.attributes[attributeName];
187+ return spanValue == null ? 1 : int .tryParse (spanValue) ?? 1 ;
188+ }
189+ }
190+
191+ TableCellElement parseTableCellElement (dom.Element element,
192+ List <StyledElement > children,
193+ ) {
194+ final cell = TableCellElement (
195+ name: element.localName,
196+ elementId: element.id,
197+ elementClasses: element.classes.toList (),
198+ children: children,
199+ node: element,
200+ );
201+ if (element.localName == "th" ) {
202+ cell.style = Style (
203+ fontWeight: FontWeight .bold,
204+ );
137205 }
206+ return cell;
138207}
139208
140209class TableStyleElement extends StyledElement {
@@ -146,9 +215,8 @@ class TableStyleElement extends StyledElement {
146215 }) : super (name: name, children: children, style: style, node: node);
147216}
148217
149- TableStyleElement parseTableDefinitionElement (
150- dom.Element element,
151- List <StyledElement > children,
218+ TableStyleElement parseTableDefinitionElement (dom.Element element,
219+ List <StyledElement > children,
152220) {
153221 switch (element.localName) {
154222 case "colgroup" :
@@ -163,9 +231,8 @@ TableStyleElement parseTableDefinitionElement(
163231 }
164232}
165233
166- LayoutElement parseLayoutElement (
167- dom.Element element,
168- List <StyledElement > children,
234+ LayoutElement parseLayoutElement (dom.Element element,
235+ List <StyledElement > children,
169236) {
170237 switch (element.localName) {
171238 case "table" :
0 commit comments