statezero 0.1.0b61__py3-none-any.whl → 0.1.0b63__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.
statezero/core/classes.py CHANGED
@@ -1,10 +1,11 @@
1
- from dataclasses import dataclass, field
1
+ from dataclasses import field
2
2
  from enum import Enum
3
- from typing import Any, Dict, List, Optional, Set, Type, Union
3
+ from typing import Any, Dict, List, Literal, Optional, Set, Type, Union, Annotated
4
4
 
5
5
  import jsonschema
6
6
  from fastapi.encoders import jsonable_encoder
7
- from pydantic import BaseModel, Field
7
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
8
+ from pydantic.dataclasses import dataclass
8
9
 
9
10
  from statezero.core.types import ORMField
10
11
 
@@ -82,7 +83,7 @@ class FieldFormat(str, Enum):
82
83
  MONEY = "money"
83
84
 
84
85
 
85
- @dataclass
86
+ @dataclass(config=ConfigDict(arbitrary_types_allowed=True))
86
87
  class AdditionalField:
87
88
  """
88
89
  Represents configuration for an additional computed field in the schema.
@@ -94,7 +95,7 @@ class AdditionalField:
94
95
  """
95
96
 
96
97
  name: str # The property/method name to pull from
97
- field: Type[ORMField] # The instantiated serializer field (e.g. CharField(max_length=255)) #type:ignore
98
+ field: ORMField # The instantiated serializer field (e.g. CharField(max_length=255)) #type:ignore
98
99
  title: Optional[str] # Optional display name override
99
100
 
100
101
  class SchemaFieldMetadata(BaseModel):
@@ -205,6 +206,213 @@ class FieldGroup:
205
206
  field_names: Optional[List[str]] = None
206
207
 
207
208
 
209
+ # =============================================================================
210
+ # Layout Elements - JSON Forms inspired layout system
211
+ # =============================================================================
212
+
213
+ class LayoutType(str, Enum):
214
+ """Types of layout elements"""
215
+ VERTICAL = "VerticalLayout"
216
+ HORIZONTAL = "HorizontalLayout"
217
+ GROUP = "Group"
218
+ CONTROL = "Control"
219
+ DISPLAY = "Display"
220
+ ALERT = "Alert"
221
+ LABEL = "Label"
222
+ DIVIDER = "Divider"
223
+
224
+
225
+ @dataclass
226
+ class Control:
227
+ """
228
+ A form control bound to a serializer field.
229
+ Uses same attribute names as FieldDisplayConfig for consistency.
230
+
231
+ Attributes:
232
+ field_name: The serializer field this control is bound to
233
+ display_component: Custom UI component name
234
+ filter_queryset: Filter options for FK/M2M fields
235
+ display_help_text: Additional help text
236
+ extra: Additional custom metadata passed to the component
237
+ label: Override the field's title
238
+ full_width: Whether this control should span full width
239
+ """
240
+ field_name: str
241
+ display_component: Optional[str] = None
242
+ filter_queryset: Optional[Dict[str, Any]] = None
243
+ display_help_text: Optional[str] = None
244
+ extra: Optional[Dict[str, Any]] = None
245
+ label: Optional[str] = None
246
+ full_width: bool = False
247
+ type: Literal["Control"] = field(default="Control", init=False)
248
+
249
+
250
+ @dataclass
251
+ class Display:
252
+ """
253
+ A display-only element that renders data from context.
254
+ Does not collect input - purely for showing information.
255
+
256
+ Attributes:
257
+ context_path: Dot-notation path to value in workflow context (e.g., "unit.access_code")
258
+ display_component: UI component to render with (e.g., "code-display", "copy-url")
259
+ label: Label to show above the display
260
+ extra: Additional custom metadata passed to the component
261
+ """
262
+ context_path: Optional[str] = None
263
+ display_component: str = "text"
264
+ label: Optional[str] = None
265
+ extra: Optional[Dict[str, Any]] = None
266
+ type: Literal["Display"] = field(default="Display", init=False)
267
+
268
+
269
+ @dataclass
270
+ class Alert:
271
+ """
272
+ An alert/info banner element.
273
+
274
+ Attributes:
275
+ severity: Alert type - "info", "warning", "error", "success"
276
+ text: Static text to display
277
+ context_path: Or pull text from context
278
+ """
279
+ severity: Literal["info", "warning", "error", "success"] = "info"
280
+ text: Optional[str] = None
281
+ context_path: Optional[str] = None
282
+ type: Literal["Alert"] = field(default="Alert", init=False)
283
+
284
+
285
+ @dataclass
286
+ class Label:
287
+ """
288
+ A static text label element.
289
+
290
+ Attributes:
291
+ text: The text to display
292
+ variant: Text style - "heading", "subheading", "body", "caption"
293
+ """
294
+ text: str
295
+ variant: Literal["heading", "subheading", "body", "caption"] = "body"
296
+ type: Literal["Label"] = field(default="Label", init=False)
297
+
298
+
299
+ @dataclass
300
+ class Divider:
301
+ """A visual separator/divider element."""
302
+ type: Literal["Divider"] = field(default="Divider", init=False)
303
+
304
+
305
+ @dataclass
306
+ class Conditional:
307
+ """
308
+ Conditionally render a layout based on form data or context.
309
+
310
+ The `when` expression is evaluated as JavaScript with access to:
311
+ - formData: Current form field values
312
+ - context: Workflow context data
313
+
314
+ Examples:
315
+ when="formData.payment_method === 'card'"
316
+ when="context.has_wifi === true"
317
+ when="formData.amount > 100"
318
+
319
+ Attributes:
320
+ when: JavaScript expression that returns a boolean
321
+ layout: Layout to render when condition is true
322
+ """
323
+ when: str
324
+ layout: "LayoutElement"
325
+ type: Literal["Conditional"] = field(default="Conditional", init=False)
326
+
327
+
328
+ @dataclass
329
+ class Tab:
330
+ """
331
+ A single tab within a Tabs container.
332
+
333
+ Attributes:
334
+ label: Tab button label
335
+ layout: Content to render when tab is active
336
+ """
337
+ label: str
338
+ layout: "LayoutElement"
339
+
340
+
341
+ @dataclass
342
+ class Tabs:
343
+ """
344
+ A tabbed container for organizing content into switchable panels.
345
+
346
+ Attributes:
347
+ tabs: List of Tab elements
348
+ default_tab: Index of initially active tab (0-based)
349
+ """
350
+ tabs: List[Tab] = field(default_factory=list)
351
+ default_tab: int = 0
352
+ type: Literal["Tabs"] = field(default="Tabs", init=False)
353
+
354
+
355
+ # Layout element union type for type hints
356
+ LayoutElement = Union[
357
+ Control, Display, Alert, Label, Divider, Conditional, Tabs,
358
+ "VerticalLayout", "HorizontalLayout", "Group"
359
+ ]
360
+
361
+
362
+ @dataclass
363
+ class VerticalLayout:
364
+ """
365
+ Stack elements vertically.
366
+
367
+ Attributes:
368
+ elements: Child layout elements
369
+ gap: Spacing between elements - "sm", "md", "lg"
370
+ """
371
+ elements: List["LayoutElement"] = field(default_factory=list)
372
+ gap: Literal["sm", "md", "lg"] = "md"
373
+ type: Literal["VerticalLayout"] = field(default="VerticalLayout", init=False)
374
+
375
+
376
+ @dataclass
377
+ class HorizontalLayout:
378
+ """
379
+ Stack elements horizontally.
380
+
381
+ Attributes:
382
+ elements: Child layout elements
383
+ gap: Spacing between elements - "sm", "md", "lg"
384
+ align: Vertical alignment - "start", "center", "end", "stretch"
385
+ """
386
+ elements: List["LayoutElement"] = field(default_factory=list)
387
+ gap: Literal["sm", "md", "lg"] = "md"
388
+ align: Literal["start", "center", "end", "stretch"] = "start"
389
+ type: Literal["HorizontalLayout"] = field(default="HorizontalLayout", init=False)
390
+
391
+
392
+ @dataclass
393
+ class Group:
394
+ """
395
+ A labeled container/section with nested layout.
396
+
397
+ Attributes:
398
+ label: Section heading
399
+ description: Section description
400
+ layout: Nested layout (defaults to VerticalLayout)
401
+ collapsible: Whether the group can be collapsed
402
+ collapsed: Initial collapsed state
403
+ """
404
+ label: str
405
+ description: Optional[str] = None
406
+ layout: Optional["LayoutElement"] = None
407
+ collapsible: bool = False
408
+ collapsed: bool = False
409
+ type: Literal["Group"] = field(default="Group", init=False)
410
+
411
+
412
+ # Convenience type for the root layout
413
+ Layout = Union[VerticalLayout, HorizontalLayout]
414
+
415
+
208
416
  @dataclass
209
417
  class DisplayMetadata:
210
418
  """
@@ -214,11 +422,16 @@ class DisplayMetadata:
214
422
  display_title: Main heading/title override
215
423
  display_description: Explanatory text about the model/action
216
424
  field_groups: Logical grouping of fields (e.g., "Contact Info", "Address Details")
425
+ - Legacy: use `layout` for more control
217
426
  field_display_configs: Per-field customization (custom components, filters, help text)
427
+ - Legacy: use Control elements in `layout` for more control
428
+ layout: Rich layout tree for complex form/display rendering. Takes precedence over
429
+ field_groups when present. Supports nesting, display-only elements, conditionals, etc.
218
430
  extra: Additional custom metadata for framework-specific or UI-specific extensions
219
431
  """
220
432
  display_title: Optional[str] = None
221
433
  display_description: Optional[str] = None
222
434
  field_groups: Optional[List[FieldGroup]] = None
223
435
  field_display_configs: Optional[List[FieldDisplayConfig]] = None
436
+ layout: Optional[Layout] = None
224
437
  extra: Optional[Dict[str, Any]] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statezero
3
- Version: 0.1.0b61
3
+ Version: 0.1.0b63
4
4
  Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
5
5
  Author-email: Robert <robert.herring@statezero.dev>
6
6
  Project-URL: homepage, https://www.statezero.dev
@@ -39,7 +39,7 @@ statezero/core/__init__.py,sha256=IaNGa9WGZ-2OopG8UTqmijDZcwpC8tIGrfkl_cvCjBk,10
39
39
  statezero/core/actions.py,sha256=MO1NN5Vtc-nu1e43x32hHrEdPRXWTYRvbC5sKPZczXQ,5385
40
40
  statezero/core/ast_parser.py,sha256=oKDchCrv2FDN2UsnC3mfI-6D3HiqXtlID5JLock6Aow,45382
41
41
  statezero/core/ast_validator.py,sha256=130IMTemJnIWd3PQHTLZYLfMFBLMmkTm37ckwHpt-QY,12214
42
- statezero/core/classes.py,sha256=ISHvp951KFHrcUAC9AvVnh4CoH_CVMyPGFqIIsEQuJg,6772
42
+ statezero/core/classes.py,sha256=UZugF4ZAqewyOtr_WMu0N9pa0U6bwTfEwYEML8Fq6qs,13272
43
43
  statezero/core/config.py,sha256=LdFlGutkJF-rsIViJwbRHVOFKWzt-33Q0Inhcba8PnY,14875
44
44
  statezero/core/context_storage.py,sha256=nyQX8dGavLeaFosApupLqsMmy1269Ederwz8PjRRjvE,846
45
45
  statezero/core/event_bus.py,sha256=jqmzTTHJCqBoQ0OnDYWt5fbpNt_z1hgZu_2TdWZ2CP4,8303
@@ -51,7 +51,7 @@ statezero/core/process_request.py,sha256=s1yys2ms856KJxV-cIfnVIdMkrOW9nLfzyLor64
51
51
  statezero/core/query_cache.py,sha256=zLRbvWw4H30Wn0JSPlPyNc1FVqhzFeu4n3dhCWILj4U,9155
52
52
  statezero/core/telemetry.py,sha256=EV2yLV6WAS-MTYCQSRQadiMgOD_ViJ_qUspgvbD0GqA,7757
53
53
  statezero/core/types.py,sha256=An57YP1sdd7u6eppXeKMoSudEn_6-Pb6UoC3IdR5E8w,916
54
- statezero-0.1.0b61.dist-info/METADATA,sha256=cgwUpm_0hE_F4Rc5FQMv3iUxAubtHl-iwJLGUeWO4Fk,9872
55
- statezero-0.1.0b61.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
56
- statezero-0.1.0b61.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
57
- statezero-0.1.0b61.dist-info/RECORD,,
54
+ statezero-0.1.0b63.dist-info/METADATA,sha256=HTBbii2bSOhFCMbb1ViV90PqzIK_tf12U0U8eB5096E,9872
55
+ statezero-0.1.0b63.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
56
+ statezero-0.1.0b63.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
57
+ statezero-0.1.0b63.dist-info/RECORD,,