pythonnative 0.6.0__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,52 +4,29 @@ Each function returns an :class:`Element` describing a native UI widget.
4
4
  These are pure data — no native views are created until the reconciler
5
5
  mounts the element tree.
6
6
 
7
- Layout properties (``width``, ``height``, ``flex``, ``margin``,
8
- ``min_width``, ``max_width``, ``min_height``, ``max_height``,
9
- ``align_self``) are supported by all components.
10
- """
11
-
12
- from typing import Any, Callable, Dict, List, Optional, Union
13
-
14
- from .element import Element
7
+ All visual and layout properties are passed via the ``style`` parameter,
8
+ which accepts a dict or a list of dicts (later entries override earlier).
15
9
 
16
- # ======================================================================
17
- # Shared helpers
18
- # ======================================================================
10
+ Layout properties supported by all components::
19
11
 
20
- PaddingValue = Union[int, float, Dict[str, Union[int, float]]]
21
- MarginValue = Union[int, float, Dict[str, Union[int, float]]]
12
+ width, height, flex, flex_grow, flex_shrink, margin,
13
+ min_width, max_width, min_height, max_height, align_self
22
14
 
15
+ Flex container properties (View / Column / Row)::
23
16
 
24
- def _filter_none(**kwargs: Any) -> Dict[str, Any]:
25
- """Return *kwargs* with ``None``-valued entries removed."""
26
- return {k: v for k, v in kwargs.items() if v is not None}
17
+ flex_direction, justify_content, align_items, overflow,
18
+ spacing, padding
27
19
 
20
+ ``View`` is the universal flex container (like React Native's ``View``).
21
+ It defaults to ``flex_direction: "column"``. ``Column`` and ``Row``
22
+ are convenience wrappers that fix the direction to ``"column"`` and
23
+ ``"row"`` respectively.
24
+ """
28
25
 
29
- def _layout_props(
30
- width: Optional[float] = None,
31
- height: Optional[float] = None,
32
- flex: Optional[float] = None,
33
- margin: Optional[MarginValue] = None,
34
- min_width: Optional[float] = None,
35
- max_width: Optional[float] = None,
36
- min_height: Optional[float] = None,
37
- max_height: Optional[float] = None,
38
- align_self: Optional[str] = None,
39
- ) -> Dict[str, Any]:
40
- """Collect common layout props into a dict (excluding Nones)."""
41
- return _filter_none(
42
- width=width,
43
- height=height,
44
- flex=flex,
45
- margin=margin,
46
- min_width=min_width,
47
- max_width=max_width,
48
- min_height=min_height,
49
- max_height=max_height,
50
- align_self=align_self,
51
- )
26
+ from typing import Any, Callable, Dict, List, Optional
52
27
 
28
+ from .element import Element
29
+ from .style import StyleValue, resolve_style
53
30
 
54
31
  # ======================================================================
55
32
  # Leaf components
@@ -59,46 +36,16 @@ def _layout_props(
59
36
  def Text(
60
37
  text: str = "",
61
38
  *,
62
- font_size: Optional[float] = None,
63
- color: Optional[str] = None,
64
- bold: bool = False,
65
- text_align: Optional[str] = None,
66
- background_color: Optional[str] = None,
67
- max_lines: Optional[int] = None,
68
- width: Optional[float] = None,
69
- height: Optional[float] = None,
70
- flex: Optional[float] = None,
71
- margin: Optional[MarginValue] = None,
72
- min_width: Optional[float] = None,
73
- max_width: Optional[float] = None,
74
- min_height: Optional[float] = None,
75
- max_height: Optional[float] = None,
76
- align_self: Optional[str] = None,
39
+ style: StyleValue = None,
77
40
  key: Optional[str] = None,
78
41
  ) -> Element:
79
- """Display text."""
80
- props = _filter_none(
81
- text=text,
82
- font_size=font_size,
83
- color=color,
84
- bold=bold or None,
85
- text_align=text_align,
86
- background_color=background_color,
87
- max_lines=max_lines,
88
- )
89
- props.update(
90
- _layout_props(
91
- width=width,
92
- height=height,
93
- flex=flex,
94
- margin=margin,
95
- min_width=min_width,
96
- max_width=max_width,
97
- min_height=min_height,
98
- max_height=max_height,
99
- align_self=align_self,
100
- )
101
- )
42
+ """Display text.
43
+
44
+ Style properties: ``font_size``, ``color``, ``bold``, ``text_align``,
45
+ ``background_color``, ``max_lines``, plus common layout props.
46
+ """
47
+ props: Dict[str, Any] = {"text": text}
48
+ props.update(resolve_style(style))
102
49
  return Element("Text", props, [], key=key)
103
50
 
104
51
 
@@ -106,46 +53,21 @@ def Button(
106
53
  title: str = "",
107
54
  *,
108
55
  on_click: Optional[Callable[[], None]] = None,
109
- color: Optional[str] = None,
110
- background_color: Optional[str] = None,
111
- font_size: Optional[float] = None,
112
56
  enabled: bool = True,
113
- width: Optional[float] = None,
114
- height: Optional[float] = None,
115
- flex: Optional[float] = None,
116
- margin: Optional[MarginValue] = None,
117
- min_width: Optional[float] = None,
118
- max_width: Optional[float] = None,
119
- min_height: Optional[float] = None,
120
- max_height: Optional[float] = None,
121
- align_self: Optional[str] = None,
57
+ style: StyleValue = None,
122
58
  key: Optional[str] = None,
123
59
  ) -> Element:
124
- """Create a tappable button."""
60
+ """Create a tappable button.
61
+
62
+ Style properties: ``color``, ``background_color``, ``font_size``,
63
+ plus common layout props.
64
+ """
125
65
  props: Dict[str, Any] = {"title": title}
126
66
  if on_click is not None:
127
67
  props["on_click"] = on_click
128
- if color is not None:
129
- props["color"] = color
130
- if background_color is not None:
131
- props["background_color"] = background_color
132
- if font_size is not None:
133
- props["font_size"] = font_size
134
68
  if not enabled:
135
69
  props["enabled"] = False
136
- props.update(
137
- _layout_props(
138
- width=width,
139
- height=height,
140
- flex=flex,
141
- margin=margin,
142
- min_width=min_width,
143
- max_width=max_width,
144
- min_height=min_height,
145
- max_height=max_height,
146
- align_self=align_self,
147
- )
148
- )
70
+ props.update(resolve_style(style))
149
71
  return Element("Button", props, [], key=key)
150
72
 
151
73
 
@@ -155,21 +77,14 @@ def TextInput(
155
77
  placeholder: str = "",
156
78
  on_change: Optional[Callable[[str], None]] = None,
157
79
  secure: bool = False,
158
- font_size: Optional[float] = None,
159
- color: Optional[str] = None,
160
- background_color: Optional[str] = None,
161
- width: Optional[float] = None,
162
- height: Optional[float] = None,
163
- flex: Optional[float] = None,
164
- margin: Optional[MarginValue] = None,
165
- min_width: Optional[float] = None,
166
- max_width: Optional[float] = None,
167
- min_height: Optional[float] = None,
168
- max_height: Optional[float] = None,
169
- align_self: Optional[str] = None,
80
+ style: StyleValue = None,
170
81
  key: Optional[str] = None,
171
82
  ) -> Element:
172
- """Create a single-line text entry field."""
83
+ """Create a single-line text entry field.
84
+
85
+ Style properties: ``font_size``, ``color``, ``background_color``,
86
+ plus common layout props.
87
+ """
173
88
  props: Dict[str, Any] = {"value": value}
174
89
  if placeholder:
175
90
  props["placeholder"] = placeholder
@@ -177,63 +92,27 @@ def TextInput(
177
92
  props["on_change"] = on_change
178
93
  if secure:
179
94
  props["secure"] = True
180
- if font_size is not None:
181
- props["font_size"] = font_size
182
- if color is not None:
183
- props["color"] = color
184
- if background_color is not None:
185
- props["background_color"] = background_color
186
- props.update(
187
- _layout_props(
188
- width=width,
189
- height=height,
190
- flex=flex,
191
- margin=margin,
192
- min_width=min_width,
193
- max_width=max_width,
194
- min_height=min_height,
195
- max_height=max_height,
196
- align_self=align_self,
197
- )
198
- )
95
+ props.update(resolve_style(style))
199
96
  return Element("TextInput", props, [], key=key)
200
97
 
201
98
 
202
99
  def Image(
203
100
  source: str = "",
204
101
  *,
205
- width: Optional[float] = None,
206
- height: Optional[float] = None,
207
102
  scale_type: Optional[str] = None,
208
- background_color: Optional[str] = None,
209
- flex: Optional[float] = None,
210
- margin: Optional[MarginValue] = None,
211
- min_width: Optional[float] = None,
212
- max_width: Optional[float] = None,
213
- min_height: Optional[float] = None,
214
- max_height: Optional[float] = None,
215
- align_self: Optional[str] = None,
103
+ style: StyleValue = None,
216
104
  key: Optional[str] = None,
217
105
  ) -> Element:
218
- """Display an image from a resource path or URL."""
219
- props = _filter_none(
220
- source=source or None,
221
- width=width,
222
- height=height,
223
- scale_type=scale_type,
224
- background_color=background_color,
225
- )
226
- props.update(
227
- _layout_props(
228
- flex=flex,
229
- margin=margin,
230
- min_width=min_width,
231
- max_width=max_width,
232
- min_height=min_height,
233
- max_height=max_height,
234
- align_self=align_self,
235
- )
236
- )
106
+ """Display an image from a resource path or URL.
107
+
108
+ Style properties: ``background_color``, plus common layout props.
109
+ """
110
+ props: Dict[str, Any] = {}
111
+ if source:
112
+ props["source"] = source
113
+ if scale_type is not None:
114
+ props["scale_type"] = scale_type
115
+ props.update(resolve_style(style))
237
116
  return Element("Image", props, [], key=key)
238
117
 
239
118
 
@@ -241,68 +120,52 @@ def Switch(
241
120
  *,
242
121
  value: bool = False,
243
122
  on_change: Optional[Callable[[bool], None]] = None,
244
- width: Optional[float] = None,
245
- height: Optional[float] = None,
246
- flex: Optional[float] = None,
247
- margin: Optional[MarginValue] = None,
248
- align_self: Optional[str] = None,
123
+ style: StyleValue = None,
249
124
  key: Optional[str] = None,
250
125
  ) -> Element:
251
126
  """Create a toggle switch."""
252
127
  props: Dict[str, Any] = {"value": value}
253
128
  if on_change is not None:
254
129
  props["on_change"] = on_change
255
- props.update(_layout_props(width=width, height=height, flex=flex, margin=margin, align_self=align_self))
130
+ props.update(resolve_style(style))
256
131
  return Element("Switch", props, [], key=key)
257
132
 
258
133
 
259
134
  def ProgressBar(
260
135
  *,
261
136
  value: float = 0.0,
262
- background_color: Optional[str] = None,
263
- width: Optional[float] = None,
264
- height: Optional[float] = None,
265
- flex: Optional[float] = None,
266
- margin: Optional[MarginValue] = None,
267
- align_self: Optional[str] = None,
137
+ style: StyleValue = None,
268
138
  key: Optional[str] = None,
269
139
  ) -> Element:
270
140
  """Show determinate progress (0.0 – 1.0)."""
271
- props = _filter_none(value=value, background_color=background_color)
272
- props.update(_layout_props(width=width, height=height, flex=flex, margin=margin, align_self=align_self))
141
+ props: Dict[str, Any] = {"value": value}
142
+ props.update(resolve_style(style))
273
143
  return Element("ProgressBar", props, [], key=key)
274
144
 
275
145
 
276
146
  def ActivityIndicator(
277
147
  *,
278
148
  animating: bool = True,
279
- width: Optional[float] = None,
280
- height: Optional[float] = None,
281
- margin: Optional[MarginValue] = None,
282
- align_self: Optional[str] = None,
149
+ style: StyleValue = None,
283
150
  key: Optional[str] = None,
284
151
  ) -> Element:
285
152
  """Show an indeterminate loading spinner."""
286
153
  props: Dict[str, Any] = {"animating": animating}
287
- props.update(_layout_props(width=width, height=height, margin=margin, align_self=align_self))
154
+ props.update(resolve_style(style))
288
155
  return Element("ActivityIndicator", props, [], key=key)
289
156
 
290
157
 
291
158
  def WebView(
292
159
  *,
293
160
  url: str = "",
294
- width: Optional[float] = None,
295
- height: Optional[float] = None,
296
- flex: Optional[float] = None,
297
- margin: Optional[MarginValue] = None,
298
- align_self: Optional[str] = None,
161
+ style: StyleValue = None,
299
162
  key: Optional[str] = None,
300
163
  ) -> Element:
301
164
  """Embed web content."""
302
165
  props: Dict[str, Any] = {}
303
166
  if url:
304
167
  props["url"] = url
305
- props.update(_layout_props(width=width, height=height, flex=flex, margin=margin, align_self=align_self))
168
+ props.update(resolve_style(style))
306
169
  return Element("WebView", props, [], key=key)
307
170
 
308
171
 
@@ -312,158 +175,147 @@ def Spacer(
312
175
  flex: Optional[float] = None,
313
176
  key: Optional[str] = None,
314
177
  ) -> Element:
315
- """Insert empty space with an optional fixed size."""
316
- props = _filter_none(size=size, flex=flex)
178
+ """Insert empty space with an optional fixed size or flex weight."""
179
+ props: Dict[str, Any] = {}
180
+ if size is not None:
181
+ props["size"] = size
182
+ if flex is not None:
183
+ props["flex"] = flex
317
184
  return Element("Spacer", props, [], key=key)
318
185
 
319
186
 
187
+ def Slider(
188
+ *,
189
+ value: float = 0.0,
190
+ min_value: float = 0.0,
191
+ max_value: float = 1.0,
192
+ on_change: Optional[Callable[[float], None]] = None,
193
+ style: StyleValue = None,
194
+ key: Optional[str] = None,
195
+ ) -> Element:
196
+ """Continuous value slider."""
197
+ props: Dict[str, Any] = {
198
+ "value": value,
199
+ "min_value": min_value,
200
+ "max_value": max_value,
201
+ }
202
+ if on_change is not None:
203
+ props["on_change"] = on_change
204
+ props.update(resolve_style(style))
205
+ return Element("Slider", props, [], key=key)
206
+
207
+
320
208
  # ======================================================================
321
209
  # Container components
322
210
  # ======================================================================
323
211
 
324
212
 
213
+ def View(
214
+ *children: Element,
215
+ style: StyleValue = None,
216
+ key: Optional[str] = None,
217
+ ) -> Element:
218
+ """Universal flex container (like React Native's ``View``).
219
+
220
+ Defaults to ``flex_direction: "column"``. Override via ``style``::
221
+
222
+ pn.View(child_a, child_b, style={"flex_direction": "row"})
223
+
224
+ Flex container properties (inside ``style``):
225
+
226
+ - ``flex_direction`` — ``"column"`` (default), ``"row"``,
227
+ ``"column_reverse"``, ``"row_reverse"``
228
+ - ``justify_content`` — main-axis distribution:
229
+ ``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
230
+ ``"space_between"``, ``"space_around"``, ``"space_evenly"``
231
+ - ``align_items`` — cross-axis alignment:
232
+ ``"stretch"`` (default), ``"flex_start"``, ``"center"``,
233
+ ``"flex_end"``
234
+ - ``overflow`` — ``"visible"`` (default) or ``"hidden"``
235
+ - ``spacing``, ``padding``, ``background_color``
236
+ """
237
+ props: Dict[str, Any] = {"flex_direction": "column"}
238
+ props.update(resolve_style(style))
239
+ return Element("View", props, list(children), key=key)
240
+
241
+
325
242
  def Column(
326
243
  *children: Element,
327
- spacing: float = 0,
328
- padding: Optional[PaddingValue] = None,
329
- alignment: Optional[str] = None,
330
- background_color: Optional[str] = None,
331
- width: Optional[float] = None,
332
- height: Optional[float] = None,
333
- flex: Optional[float] = None,
334
- margin: Optional[MarginValue] = None,
335
- min_width: Optional[float] = None,
336
- max_width: Optional[float] = None,
337
- min_height: Optional[float] = None,
338
- max_height: Optional[float] = None,
339
- align_self: Optional[str] = None,
244
+ style: StyleValue = None,
340
245
  key: Optional[str] = None,
341
246
  ) -> Element:
342
- """Arrange children vertically."""
343
- props = _filter_none(
344
- spacing=spacing or None,
345
- padding=padding,
346
- alignment=alignment,
347
- background_color=background_color,
348
- )
349
- props.update(
350
- _layout_props(
351
- width=width,
352
- height=height,
353
- flex=flex,
354
- margin=margin,
355
- min_width=min_width,
356
- max_width=max_width,
357
- min_height=min_height,
358
- max_height=max_height,
359
- align_self=align_self,
360
- )
361
- )
247
+ """Arrange children vertically (``flex_direction: "column"``).
248
+
249
+ Convenience wrapper around :func:`View`. The direction is fixed;
250
+ use :func:`View` directly if you need ``flex_direction: "row"``.
251
+
252
+ Style properties: ``spacing``, ``padding``, ``align_items``,
253
+ ``justify_content``, ``background_color``, ``overflow``,
254
+ plus common layout props.
255
+
256
+ ``align_items`` controls cross-axis (horizontal) alignment:
257
+ ``"stretch"`` (default), ``"flex_start"``/``"leading"``,
258
+ ``"center"``, ``"flex_end"``/``"trailing"``.
259
+
260
+ ``justify_content`` controls main-axis (vertical) distribution:
261
+ ``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
262
+ ``"space_between"``, ``"space_around"``, ``"space_evenly"``.
263
+ """
264
+ props: Dict[str, Any] = {"flex_direction": "column"}
265
+ props.update(resolve_style(style))
266
+ props["flex_direction"] = "column"
362
267
  return Element("Column", props, list(children), key=key)
363
268
 
364
269
 
365
270
  def Row(
366
271
  *children: Element,
367
- spacing: float = 0,
368
- padding: Optional[PaddingValue] = None,
369
- alignment: Optional[str] = None,
370
- background_color: Optional[str] = None,
371
- width: Optional[float] = None,
372
- height: Optional[float] = None,
373
- flex: Optional[float] = None,
374
- margin: Optional[MarginValue] = None,
375
- min_width: Optional[float] = None,
376
- max_width: Optional[float] = None,
377
- min_height: Optional[float] = None,
378
- max_height: Optional[float] = None,
379
- align_self: Optional[str] = None,
272
+ style: StyleValue = None,
380
273
  key: Optional[str] = None,
381
274
  ) -> Element:
382
- """Arrange children horizontally."""
383
- props = _filter_none(
384
- spacing=spacing or None,
385
- padding=padding,
386
- alignment=alignment,
387
- background_color=background_color,
388
- )
389
- props.update(
390
- _layout_props(
391
- width=width,
392
- height=height,
393
- flex=flex,
394
- margin=margin,
395
- min_width=min_width,
396
- max_width=max_width,
397
- min_height=min_height,
398
- max_height=max_height,
399
- align_self=align_self,
400
- )
401
- )
275
+ """Arrange children horizontally (``flex_direction: "row"``).
276
+
277
+ Convenience wrapper around :func:`View`. The direction is fixed;
278
+ use :func:`View` directly if you need ``flex_direction: "column"``.
279
+
280
+ Style properties: ``spacing``, ``padding``, ``align_items``,
281
+ ``justify_content``, ``background_color``, ``overflow``,
282
+ plus common layout props.
283
+
284
+ ``align_items`` controls cross-axis (vertical) alignment:
285
+ ``"stretch"`` (default), ``"flex_start"``/``"top"``,
286
+ ``"center"``, ``"flex_end"``/``"bottom"``.
287
+
288
+ ``justify_content`` controls main-axis (horizontal) distribution:
289
+ ``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
290
+ ``"space_between"``, ``"space_around"``, ``"space_evenly"``.
291
+ """
292
+ props: Dict[str, Any] = {"flex_direction": "row"}
293
+ props.update(resolve_style(style))
294
+ props["flex_direction"] = "row"
402
295
  return Element("Row", props, list(children), key=key)
403
296
 
404
297
 
405
298
  def ScrollView(
406
299
  child: Optional[Element] = None,
407
300
  *,
408
- background_color: Optional[str] = None,
409
- width: Optional[float] = None,
410
- height: Optional[float] = None,
411
- flex: Optional[float] = None,
412
- margin: Optional[MarginValue] = None,
413
- align_self: Optional[str] = None,
301
+ style: StyleValue = None,
414
302
  key: Optional[str] = None,
415
303
  ) -> Element:
416
304
  """Wrap a single child in a scrollable container."""
417
305
  children = [child] if child is not None else []
418
- props = _filter_none(background_color=background_color)
419
- props.update(_layout_props(width=width, height=height, flex=flex, margin=margin, align_self=align_self))
306
+ props: Dict[str, Any] = {}
307
+ props.update(resolve_style(style))
420
308
  return Element("ScrollView", props, children, key=key)
421
309
 
422
310
 
423
- def View(
424
- *children: Element,
425
- background_color: Optional[str] = None,
426
- padding: Optional[PaddingValue] = None,
427
- width: Optional[float] = None,
428
- height: Optional[float] = None,
429
- flex: Optional[float] = None,
430
- margin: Optional[MarginValue] = None,
431
- min_width: Optional[float] = None,
432
- max_width: Optional[float] = None,
433
- min_height: Optional[float] = None,
434
- max_height: Optional[float] = None,
435
- align_self: Optional[str] = None,
436
- key: Optional[str] = None,
437
- ) -> Element:
438
- """Generic container view (``UIView`` / ``android.view.View``)."""
439
- props = _filter_none(
440
- background_color=background_color,
441
- padding=padding,
442
- )
443
- props.update(
444
- _layout_props(
445
- width=width,
446
- height=height,
447
- flex=flex,
448
- margin=margin,
449
- min_width=min_width,
450
- max_width=max_width,
451
- min_height=min_height,
452
- max_height=max_height,
453
- align_self=align_self,
454
- )
455
- )
456
- return Element("View", props, list(children), key=key)
457
-
458
-
459
311
  def SafeAreaView(
460
312
  *children: Element,
461
- background_color: Optional[str] = None,
462
- padding: Optional[PaddingValue] = None,
313
+ style: StyleValue = None,
463
314
  key: Optional[str] = None,
464
315
  ) -> Element:
465
316
  """Container that respects safe area insets (notch, status bar)."""
466
- props = _filter_none(background_color=background_color, padding=padding)
317
+ props: Dict[str, Any] = {}
318
+ props.update(resolve_style(style))
467
319
  return Element("SafeAreaView", props, list(children), key=key)
468
320
 
469
321
 
@@ -472,7 +324,7 @@ def Modal(
472
324
  visible: bool = False,
473
325
  on_dismiss: Optional[Callable[[], None]] = None,
474
326
  title: Optional[str] = None,
475
- background_color: Optional[str] = None,
327
+ style: StyleValue = None,
476
328
  key: Optional[str] = None,
477
329
  ) -> Element:
478
330
  """Overlay modal dialog.
@@ -484,34 +336,10 @@ def Modal(
484
336
  props["on_dismiss"] = on_dismiss
485
337
  if title is not None:
486
338
  props["title"] = title
487
- if background_color is not None:
488
- props["background_color"] = background_color
339
+ props.update(resolve_style(style))
489
340
  return Element("Modal", props, list(children), key=key)
490
341
 
491
342
 
492
- def Slider(
493
- *,
494
- value: float = 0.0,
495
- min_value: float = 0.0,
496
- max_value: float = 1.0,
497
- on_change: Optional[Callable[[float], None]] = None,
498
- width: Optional[float] = None,
499
- margin: Optional[MarginValue] = None,
500
- align_self: Optional[str] = None,
501
- key: Optional[str] = None,
502
- ) -> Element:
503
- """Continuous value slider."""
504
- props: Dict[str, Any] = {
505
- "value": value,
506
- "min_value": min_value,
507
- "max_value": max_value,
508
- }
509
- if on_change is not None:
510
- props["on_change"] = on_change
511
- props.update(_layout_props(width=width, margin=margin, align_self=align_self))
512
- return Element("Slider", props, [], key=key)
513
-
514
-
515
343
  def Pressable(
516
344
  child: Optional[Element] = None,
517
345
  *,
@@ -529,26 +357,43 @@ def Pressable(
529
357
  return Element("Pressable", props, children, key=key)
530
358
 
531
359
 
360
+ def ErrorBoundary(
361
+ child: Optional[Element] = None,
362
+ *,
363
+ fallback: Optional[Any] = None,
364
+ key: Optional[str] = None,
365
+ ) -> Element:
366
+ """Catch render errors in *child* and display *fallback* instead.
367
+
368
+ *fallback* may be an ``Element`` or a callable that receives the
369
+ exception and returns an ``Element``::
370
+
371
+ pn.ErrorBoundary(
372
+ MyRiskyComponent(),
373
+ fallback=lambda err: pn.Text(f"Error: {err}"),
374
+ )
375
+ """
376
+ props: Dict[str, Any] = {}
377
+ if fallback is not None:
378
+ props["__fallback__"] = fallback
379
+ children = [child] if child is not None else []
380
+ return Element("__ErrorBoundary__", props, children, key=key)
381
+
382
+
532
383
  def FlatList(
533
384
  *,
534
385
  data: Optional[List[Any]] = None,
535
386
  render_item: Optional[Callable[[Any, int], Element]] = None,
536
387
  key_extractor: Optional[Callable[[Any, int], str]] = None,
537
388
  separator_height: float = 0,
538
- background_color: Optional[str] = None,
539
- width: Optional[float] = None,
540
- height: Optional[float] = None,
541
- flex: Optional[float] = None,
542
- margin: Optional[MarginValue] = None,
543
- align_self: Optional[str] = None,
389
+ style: StyleValue = None,
544
390
  key: Optional[str] = None,
545
391
  ) -> Element:
546
392
  """Scrollable list that renders items from *data* using *render_item*.
547
393
 
548
394
  Each item is rendered by calling ``render_item(item, index)``. If
549
395
  ``key_extractor`` is provided, it is called as ``key_extractor(item, index)``
550
- to produce a stable key for each child element. This enables the
551
- reconciler to preserve widget state across data changes.
396
+ to produce a stable key for each child element.
552
397
  """
553
398
  items: List[Element] = []
554
399
  for i, item in enumerate(data or []):
@@ -557,7 +402,7 @@ def FlatList(
557
402
  el = Element(el.type, el.props, el.children, key=key_extractor(item, i))
558
403
  items.append(el)
559
404
 
560
- inner = Column(*items, spacing=separator_height)
561
- sv_props = _filter_none(background_color=background_color)
562
- sv_props.update(_layout_props(width=width, height=height, flex=flex, margin=margin, align_self=align_self))
405
+ inner = Column(*items, style={"spacing": separator_height} if separator_height else None)
406
+ sv_props: Dict[str, Any] = {}
407
+ sv_props.update(resolve_style(style))
563
408
  return Element("ScrollView", sv_props, [inner], key=key)