ex4nicegui 0.5.2__tar.gz → 0.5.3__tar.gz

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.
Files changed (147) hide show
  1. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/PKG-INFO +53 -33
  2. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/README.md +52 -32
  3. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/__init__.py +3 -1
  4. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/gsap/__init__.py +2 -0
  5. ex4nicegui-0.5.3/ex4nicegui/gsap/timeline.js +56 -0
  6. ex4nicegui-0.5.3/ex4nicegui/gsap/timeline.py +78 -0
  7. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/__init__.py +6 -1
  8. ex4nicegui-0.5.3/ex4nicegui/reactive/officials/circular_progress.py +64 -0
  9. ex4nicegui-0.5.3/ex4nicegui/reactive/officials/column.py +36 -0
  10. ex4nicegui-0.5.3/ex4nicegui/reactive/officials/knob.py +75 -0
  11. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/number.py +1 -2
  12. ex4nicegui-0.5.3/ex4nicegui/reactive/officials/row.py +35 -0
  13. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/utils.py +40 -1
  14. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/vfor.py +19 -39
  15. ex4nicegui-0.5.3/ex4nicegui/reactive/vmodel.py +210 -0
  16. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/signals.py +12 -2
  17. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/PKG-INFO +53 -33
  18. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/SOURCES.txt +5 -0
  19. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/requires.txt +2 -1
  20. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/setup.py +2 -2
  21. ex4nicegui-0.5.2/ex4nicegui/reactive/officials/column.py +0 -21
  22. ex4nicegui-0.5.2/ex4nicegui/reactive/officials/row.py +0 -21
  23. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/LICENSE +0 -0
  24. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/__init__.py +0 -0
  25. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/dataSource.py +0 -0
  26. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/dataSourceFacade.py +0 -0
  27. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/__init__.py +0 -0
  28. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/containers.py +0 -0
  29. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/layouts.py +0 -0
  30. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/models.py +0 -0
  31. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/text.py +0 -0
  32. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_aggrid.py +0 -0
  33. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_date_picker.js +0 -0
  34. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_date_picker.py +0 -0
  35. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_echarts.py +0 -0
  36. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_radio.py +0 -0
  37. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_range.py +0 -0
  38. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_select.py +0 -0
  39. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_slider.py +0 -0
  40. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/elements/ui_table.py +0 -0
  41. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/index.py +0 -0
  42. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/protocols.py +0 -0
  43. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/bi/types.py +0 -0
  44. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/experimental_/__init__.py +0 -0
  45. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/experimental_/gridLayout/__init__.py +0 -0
  46. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/experimental_/gridLayout/index.py +0 -0
  47. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/gsap/gsap.py +0 -0
  48. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/gsap/wrapGsap.js +0 -0
  49. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/__init__.py +0 -0
  50. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/gridFlex/GridFlex.js +0 -0
  51. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/gridFlex/__init__.py +0 -0
  52. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/gridFlex/gridFlex.py +0 -0
  53. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/gridFlex/utils.py +0 -0
  54. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/rxFlex/__init__.py +0 -0
  55. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/rxFlex/index.py +0 -0
  56. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/layout/rxFlex/types.py +0 -0
  57. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/__init__.py +0 -0
  58. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/__init__.py +0 -0
  59. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-color.ems.js +0 -0
  60. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-dispatch.ems.js +0 -0
  61. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-drag.ems.js +0 -0
  62. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-ease.ems.js +0 -0
  63. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-interpolate.ems.js +0 -0
  64. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-selection.ems.js +0 -0
  65. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-timer.ems.js +0 -0
  66. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-transition.ems.js +0 -0
  67. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/d3/d3-zoom.ems.js +0 -0
  68. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/CSSPlugin.js +0 -0
  69. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/CSSRulePlugin.js +0 -0
  70. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/CustomEase.js +0 -0
  71. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/Draggable.js +0 -0
  72. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/EasePack.js +0 -0
  73. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/EaselPlugin.js +0 -0
  74. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/Flip.js +0 -0
  75. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/MotionPathPlugin.js +0 -0
  76. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/Observer.js +0 -0
  77. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/PixiPlugin.js +0 -0
  78. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/ScrollToPlugin.js +0 -0
  79. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/ScrollTrigger.js +0 -0
  80. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/TextPlugin.js +0 -0
  81. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/__init__.py +0 -0
  82. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/all.js +0 -0
  83. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/gsap-core.js +0 -0
  84. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/utils/__init__.py +0 -0
  85. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/utils/matrix.js +0 -0
  86. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/utils/paths.js +0 -0
  87. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/libs/gsap/utils/strings.js +0 -0
  88. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/EChartsComponent/ECharts.js +0 -0
  89. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/EChartsComponent/ECharts.py +0 -0
  90. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/EChartsComponent/__init__.py +0 -0
  91. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/UseDraggable/UseDraggable.js +0 -0
  92. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/UseDraggable/UseDraggable.py +0 -0
  93. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/UseDraggable/__init__.py +0 -0
  94. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/dropZone/__init__.py +0 -0
  95. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/dropZone/dropZone.js +0 -0
  96. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/dropZone/dropZone.py +0 -0
  97. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/fileWatcher.py +0 -0
  98. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/local_file_picker.py +0 -0
  99. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/mermaid/__init__.py +0 -0
  100. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/mermaid/mermaid.js +0 -0
  101. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/mermaid/mermaid.py +0 -0
  102. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/__init__.py +0 -0
  103. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/aggrid.py +0 -0
  104. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/base.py +0 -0
  105. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/button.py +0 -0
  106. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/card.py +0 -0
  107. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/checkbox.py +0 -0
  108. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/color_picker.py +0 -0
  109. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/date.py +0 -0
  110. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/drawer.py +0 -0
  111. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/echarts.py +0 -0
  112. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/expansion.py +0 -0
  113. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/grid.py +0 -0
  114. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/html.js +0 -0
  115. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/html.py +0 -0
  116. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/icon.py +0 -0
  117. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/image.py +0 -0
  118. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/input.py +0 -0
  119. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/label.py +0 -0
  120. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/linear_progress.py +0 -0
  121. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/radio.py +0 -0
  122. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/select.py +0 -0
  123. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/slider.py +0 -0
  124. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/switch.py +0 -0
  125. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/table.py +0 -0
  126. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/textarea.py +0 -0
  127. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/upload.py +0 -0
  128. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/officials/utils.py +0 -0
  129. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/q_pagination.py +0 -0
  130. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/rxui.py +0 -0
  131. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/useMouse/UseMouse.js +0 -0
  132. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/useMouse/UseMouse.py +0 -0
  133. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/useMouse/__init__.py +0 -0
  134. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/usePagination.py +0 -0
  135. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/reactive/vfor.js +0 -0
  136. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/tools/__init__.py +0 -0
  137. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/tools/debug.py +0 -0
  138. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/__init__.py +0 -0
  139. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/apiEffect.py +0 -0
  140. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/clientScope.py +0 -0
  141. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/common.py +0 -0
  142. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/effect.py +0 -0
  143. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui/utils/scheduler.py +0 -0
  144. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/dependency_links.txt +0 -0
  145. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/not-zip-safe +0 -0
  146. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/ex4nicegui.egg-info/top_level.txt +0 -0
  147. {ex4nicegui-0.5.2 → ex4nicegui-0.5.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ex4nicegui
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Extension library based on nicegui, providing data responsive,BI functionality modules
5
5
  Home-page:
6
6
  Author: carson_jia
@@ -386,16 +386,58 @@ ui.button("change b", on_click=change_b)
386
386
 
387
387
  ---
388
388
 
389
+ ## functionality
390
+
391
+ ### vmodel
392
+ Create two-way bindings on form input elements or components.
393
+
394
+ Bidirectional bindings are supported by default for `ref` simple value types
395
+ ```python
396
+ from ex4nicegui import rxui, to_ref, deep_ref
397
+
398
+ data = to_ref("init")
399
+
400
+ rxui.label(lambda: f"{data.value=}")
401
+ # two-way binding
402
+ rxui.input(value=data)
403
+ ```
404
+ - Simple value types are generally immutable values such as `str`, `int`, etc.
405
+
406
+ When using complex data structures, `deep_ref` is used to keep nested values responsive.
407
+ ```python
408
+ data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
409
+
410
+ rxui.label(lambda: f"{data.value=!s}")
411
+
412
+ # No binding effect
413
+ rxui.input(value=data.value["a"])
414
+
415
+ # readonly binding
416
+ rxui.input(value=lambda: data.value["a"])
417
+
418
+ # two-way binding
419
+ rxui.input(value=rxui.vmodel(data.value["a"]))
420
+ ```
421
+
422
+ - The first input box will be completely unresponsive because the code is equivalent to passing in a value `1` directly
423
+ - The second input box will get read responsiveness due to the use of a function.
424
+ - The third input box, wrapped in `rxui.vmodel`, can be bi-directionally bound.
425
+
426
+ Most use `vmodel` with `vfor`, see [todo list examples](./examples/todomvc/)
427
+
428
+ ---
429
+
389
430
  ### vfor
390
431
  Render list components based on list responsive data. Each component is updated on demand. Data items support dictionaries or objects of any type
391
432
 
392
433
  ```python
393
434
  from nicegui import ui
394
435
  from ex4nicegui.reactive import rxui
395
- from ex4nicegui import to_ref, ref_computed
436
+ from ex4nicegui import deep_ref, ref_computed
437
+ from typing import Dict
396
438
 
397
439
  # refs
398
- items = to_ref(
440
+ items = deep_ref(
399
441
  [
400
442
  {"id": 1, "message": "foo", "done": False},
401
443
  {"id": 2, "message": "bar", "done": True},
@@ -411,7 +453,6 @@ def done_count_info():
411
453
  def check():
412
454
  for item in items.value:
413
455
  item["done"] = not item["done"]
414
- items.value = items.value
415
456
 
416
457
 
417
458
  # ui
@@ -420,54 +461,33 @@ ui.button("check", on_click=check)
420
461
 
421
462
 
422
463
  @rxui.vfor(items,key='id')
423
- def _(store: rxui.VforStore):
464
+ def _(store: rxui.VforStore[Dict]):
424
465
  # function to build the interface for each row of data
425
- msg_ref = store.get("message") # Get responsive object with `store.get`
466
+ item = store.get() # Get responsive object with `store.get`
467
+ mes = rxui.vmodel(item.value['message'])
426
468
 
427
469
  # Enter the content of the input box,
428
470
  # you can see the title of the radio box changes synchronously
429
471
  with ui.card():
430
- rxui.input(value=msg_ref)
431
- rxui.checkbox(text=msg_ref, value=store.get("done"))
472
+ with ui.row():
473
+ rxui.input(value=mes)
474
+ rxui.label(lambda: f"{mes.value=!s}")
475
+ rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))
432
476
 
433
477
  ```
434
478
 
435
479
  - `rxui.vfor` decorator to custom function
436
480
  - The first argument is passed to the responsive list. Each item in the list can be a dictionary or other object (`dataclasses` etc.)
437
481
  - Second parameter `key`: In order to be able to keep track of the identity of each node, and thus reuse and reorder existing elements, you can provide a unique key for the block corresponding to each element. The default(`None`) is to use the list element index.
438
- - The custom function takes one argument. The current row's attribute can be retrieved via `store.get`, which is a responsive object.
482
+ - The custom function takes one argument. The current row's can be retrieved via `store.get`, which is a responsive object.
439
483
 
440
484
 
441
485
  > vfor are created only when new data is added.
442
486
 
443
- In the above example, you'll notice that when the checkbox is clicked, the text of the number of completed counts (`done_count_info`) doesn't change synchronously
444
-
445
- This is because responsive data changes in the `vfor` function do not affect the data source list. This is a restriction to prevent writing overly complex bi-directional data flow response logic.
446
-
447
- We should make changes to the data source list via events in the function
448
-
449
- ```python
450
- ...
451
-
452
- @rxui.vfor(items, key="id")
453
- def _(store: rxui.VforStore):
454
- msg_ref = store.get("message")
455
-
456
- def on_check_change(e):
457
- items.value[store.row_index]["done"] = e.value
458
- items.value = items.value
459
-
460
- with ui.card():
461
- rxui.input(value=msg_ref)
462
- rxui.checkbox(text=msg_ref, value=store.get("done"),on_change=on_check_change)
463
-
464
- ```
465
487
 
466
488
  ---
467
489
 
468
490
 
469
- ## functionality
470
-
471
491
  ### Bind class names
472
492
 
473
493
  All component classes provide `bind_classes` for binding `class`, supporting three different data structures.
@@ -383,16 +383,56 @@ ui.button("change b", on_click=change_b)
383
383
 
384
384
  ## 组件功能
385
385
 
386
+ ### vmodel
387
+ 在表单输入元素或组件上创建双向绑定。
388
+
389
+ 简单值类型的 `ref` 默认支持双向绑定
390
+ ```python
391
+ from ex4nicegui import rxui, to_ref, deep_ref
392
+
393
+ data = to_ref("init")
394
+
395
+ rxui.label(lambda: f"{data.value=}")
396
+ # 默认就是双向绑定
397
+ rxui.input(value=data)
398
+ ```
399
+
400
+ - 简单值类型一般是 `str`,`int` 等不可变值类型
401
+
402
+ 当使用复杂数据结构时,会使用 `deep_ref` 保持嵌套值的响应性
403
+ ```python
404
+ data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
405
+
406
+ rxui.label(lambda: f"{data.value=!s}")
407
+
408
+ # 当前版本没有任何绑定效果.或许未来的版本可以解决
409
+ rxui.input(value=data.value["a"])
410
+
411
+ # 只读绑定.其他途径修改了 `data.value["a"]` ,此输入框会同步,但反过来不行
412
+ rxui.input(value=lambda: data.value["a"])
413
+
414
+ # 要使用 vmodel 才能双向绑定
415
+ rxui.input(value=rxui.vmodel(data.value["a"]))
416
+ ```
417
+
418
+ - 第一个输入框将完全失去响应性,因为代码等价于直接传入一个数值`1`
419
+ - 第二个输入框由于使用函数,将得到读取响应性(第三个输入框输入值,将得到同步)
420
+ - 第三个输入框,使用 `rxui.vmodel` 包裹,即可实现双向绑定
421
+
422
+ 多数在配合 `vfor` 时使用 `vmodel`,可参考 [todo list 案例](./examples/todomvc/)
423
+
424
+
386
425
  ### vfor
387
426
  基于列表响应式数据,渲染列表组件。每项组件按需更新。数据项支持字典或任意类型对象
388
427
 
389
428
  ```python
390
429
  from nicegui import ui
391
430
  from ex4nicegui.reactive import rxui
392
- from ex4nicegui import to_ref, ref_computed
431
+ from ex4nicegui import deep_ref, ref_computed
432
+ from typing import Dict
393
433
 
394
434
  # refs
395
- items = to_ref(
435
+ items = deep_ref(
396
436
  [
397
437
  {"id": 1, "message": "foo", "done": False},
398
438
  {"id": 2, "message": "bar", "done": True},
@@ -408,55 +448,35 @@ def done_count_info():
408
448
  def check():
409
449
  for item in items.value:
410
450
  item["done"] = not item["done"]
411
- items.value = items.value
412
451
 
413
452
 
414
453
  # ui
415
454
  rxui.label(done_count_info)
416
455
  ui.button("check", on_click=check)
417
456
 
418
- @rxui.vfor(items, key="id")
419
- def _(store: rxui.VforStore):
457
+
458
+ @rxui.vfor(items,key='id')
459
+ def _(store: rxui.VforStore[Dict]):
420
460
  # 函数中构建每一行数据的界面
421
- msg_ref = store.get("message") # 通过 store.get 获取对应行的属性响应式对象
461
+ item = store.get() # 通过 store.get 获取对应行的响应式对象(相当于每行的数据 to_ref(...))
462
+ mes = rxui.vmodel(item.value['message']) # 复杂结构默认没有双向绑定,需要使用 `vmodel`
422
463
 
423
464
  # 输入框输入内容,可以看到单选框的标题同步变化
424
465
  with ui.card():
425
- rxui.input(value=msg_ref)
426
- rxui.checkbox(text=msg_ref, value=store.get("done"))
466
+ with ui.row():
467
+ rxui.input(value=mes)
468
+ rxui.label(lambda: f"{mes.value=!s}")
469
+ rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))
427
470
 
428
471
  ```
429
472
 
430
473
  - `rxui.vfor` 装饰器到自定义函数
431
474
  - 第一个参数传入响应式列表。列表中每一项可以是字典或其他对象(`dataclasses` 等等)
432
475
  - 第二个参数 `key`: 为了可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你可以为每个元素对应的块提供一个唯一的 key 。默认情况使用列表元素索引。
433
- - 自定义函数带有一个参数。通过 `store.get` 可以获取当前行对应的属性,此为响应式对象
476
+ - 自定义函数带有一个参数。通过 `store.get` 可以获取当前行的响应式对象
434
477
 
435
478
  > vfor 渲染的项目,只有在新增数据时,才会创建
436
479
 
437
- 上述的例子中,你会发现,当点击 checkbox 时,完成数量的文本(`done_count_info`)并没有同步变化
438
-
439
- 因为 `vfor` 函数中对响应式数据修改,不会影响数据源列表。这是为了防止写出过于复杂的双向数据流响应逻辑而限制。
440
-
441
- 我们应该在函数中通过事件,对数据源列表做修改
442
-
443
- ```python
444
- ...
445
-
446
- @rxui.vfor(items, key="id")
447
- def _(store: rxui.VforStore):
448
- msg_ref = store.get("message")
449
-
450
- def on_check_change(e):
451
- items.value[store.row_index]["done"] = e.value
452
- items.value = items.value
453
-
454
- with ui.card():
455
- rxui.input(value=msg_ref)
456
- rxui.checkbox(text=msg_ref, value=store.get("done"),on_change=on_check_change)
457
-
458
- ```
459
-
460
480
 
461
481
  ---
462
482
 
@@ -17,10 +17,12 @@ from ex4nicegui.utils.signals import (
17
17
  deep_ref,
18
18
  is_setter_ref,
19
19
  batch,
20
+ is_reactive,
20
21
  )
21
22
 
22
23
 
23
24
  __all__ = [
25
+ "is_reactive",
24
26
  "rxui",
25
27
  "ref_computed",
26
28
  "effect",
@@ -41,4 +43,4 @@ __all__ = [
41
43
  "is_setter_ref",
42
44
  ]
43
45
 
44
- __version__ = "0.5.2"
46
+ __version__ = "0.5.3"
@@ -1,4 +1,5 @@
1
1
  from .gsap import set_defaults, from_, to, new, run_script
2
+ from .timeline import Timeline as timeline
2
3
 
3
4
 
4
5
  __all__ = [
@@ -7,4 +8,5 @@ __all__ = [
7
8
  "to",
8
9
  "new",
9
10
  "run_script",
11
+ "timeline",
10
12
  ]
@@ -0,0 +1,56 @@
1
+ import { gsap } from "gsap";
2
+ import { convertDynamicProperties } from "../../static/utils/dynamic_properties.js";
3
+
4
+
5
+ export default {
6
+ template: `<template></template>`,
7
+ data() {
8
+
9
+ const tl = gsap.timeline(this.defaults)
10
+ this.tl = tl
11
+
12
+ return {
13
+ }
14
+
15
+ },
16
+ mounted() {
17
+ document.addEventListener('DOMContentLoaded', () => {
18
+ /**
19
+ * @type any[]
20
+ */
21
+ const tasks = this.tasks
22
+ tasks.forEach(t => {
23
+ this[t.method](t.targets, t.vars, t.position)
24
+ })
25
+
26
+ this.scriptTasks.forEach(script => {
27
+ const fn = new Function('return ' + script)()
28
+ fn(this.tl, gsap)
29
+ })
30
+
31
+ })
32
+ },
33
+
34
+ methods: {
35
+ from(targets, vars, position) {
36
+ convertDynamicProperties(vars, false)
37
+ this.tl.from(targets, vars, position)
38
+ },
39
+ to(targets, vars, position) {
40
+ convertDynamicProperties(vars, false)
41
+ this.tl.to(targets, vars, position)
42
+ },
43
+ runScript(script) {
44
+ const fn = new Function('return ' + script)()
45
+ fn(this.tl, gsap)
46
+ },
47
+ callTimeline(name, ...args) {
48
+ this.tl[name](...args)
49
+ }
50
+ },
51
+ props: {
52
+ defaults: Object,
53
+ tasks: Array,
54
+ scriptTasks: Array,
55
+ },
56
+ };
@@ -0,0 +1,78 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Dict, Optional, Union
4
+ from nicegui.element import Element
5
+ from nicegui import context as ng_context
6
+
7
+
8
+ class Timeline(
9
+ Element,
10
+ component="timeline.js",
11
+ exposed_libraries=["../libs/gsap/gsap.mjs"],
12
+ extra_libraries=["../libs/gsap/*.js", "../libs/gsap/utils/*.js"],
13
+ ):
14
+ def __init__(
15
+ self,
16
+ defaults: Optional[Dict] = None,
17
+ ) -> None:
18
+ super().__init__()
19
+ self._props["defaults"] = defaults or {}
20
+ self._props["tasks"] = []
21
+ self._props["scriptTasks"] = []
22
+
23
+ def from_(self, targets: str, vars: Dict, position: Optional[str] = None):
24
+ self.__try_run_task("from", targets, vars, position)
25
+ return self
26
+
27
+ def to(self, targets: str, vars: Dict, position: Optional[str] = None):
28
+ self.__try_run_task("to", targets, vars, position)
29
+ return self
30
+
31
+ def run_script(self, script: Union[str, Path]):
32
+ if isinstance(script, Path):
33
+ script = script.read_text("utf8")
34
+
35
+ script = os.linesep.join([s for s in script.splitlines() if s])
36
+ self.__try_run_script_task(script)
37
+ return self
38
+
39
+ def __try_run_script_task(self, script: str):
40
+ def fn():
41
+ self.run_method("runScript", script)
42
+
43
+ if ng_context.get_client().has_socket_connection:
44
+ fn()
45
+ else:
46
+ tasks = self._props["scriptTasks"]
47
+ tasks.append(script)
48
+
49
+ def __try_run_task(
50
+ self, name: str, targets: str, vars: Dict, position: Optional[str] = None
51
+ ):
52
+ def fn():
53
+ self.run_method(name, targets, vars)
54
+
55
+ if ng_context.get_client().has_socket_connection:
56
+ fn()
57
+ else:
58
+ tasks = self._props["tasks"]
59
+ tasks.append(
60
+ {"method": name, "targets": targets, "vars": vars, "position": position}
61
+ )
62
+
63
+ def pause(self):
64
+ self.run_method("callTimeline", "pause")
65
+
66
+ def play(self):
67
+ self.run_method("callTimeline", "play")
68
+
69
+ def resume(
70
+ self,
71
+ ):
72
+ self.run_method("callTimeline", "resume")
73
+
74
+ def seek(self, position: Optional[str] = None, suppressEvents=True):
75
+ self.run_method("callTimeline", "seek", position, suppressEvents)
76
+
77
+ def reverse(self):
78
+ self.run_method("callTimeline", "reverse")
@@ -44,7 +44,8 @@ from .officials.number import NumberBindableUi as number
44
44
  from .officials.grid import GridBindableUi as grid
45
45
  from .officials.expansion import ExpansionBindableUi as expansion
46
46
  from .officials.linear_progress import LinearProgressBindableUi as linear_progress
47
-
47
+ from .officials.knob import KnobBindableUi as knob
48
+ from .officials.circular_progress import CircularProgressBindableUi as circular_progress
48
49
  from .q_pagination import PaginationBindableUi as q_pagination
49
50
 
50
51
  from .local_file_picker import local_file_picker
@@ -56,11 +57,14 @@ from .dropZone.dropZone import use_drag_zone
56
57
  from .fileWatcher import FilesWatcher
57
58
  from .mermaid.mermaid import Mermaid as mermaid
58
59
  from .vfor import vfor, VforStore
60
+ from .vmodel import vmodel
59
61
 
60
62
  pagination = q_pagination
61
63
 
62
64
 
63
65
  __all__ = [
66
+ "circular_progress",
67
+ "knob",
64
68
  "UploadResult",
65
69
  "local_file_picker",
66
70
  "use_draggable",
@@ -70,6 +74,7 @@ __all__ = [
70
74
  "FilesWatcher",
71
75
  "vfor",
72
76
  "VforStore",
77
+ "vmodel",
73
78
  "html",
74
79
  "aggird",
75
80
  "button",
@@ -0,0 +1,64 @@
1
+ from typing import (
2
+ Optional,
3
+ )
4
+ from ex4nicegui.reactive.utils import ParameterClassifier
5
+ from ex4nicegui.utils.apiEffect import ui_effect
6
+
7
+ from ex4nicegui.utils.signals import (
8
+ ReadonlyRef,
9
+ _TMaybeRef as TMaybeRef,
10
+ to_value,
11
+ )
12
+ from nicegui import ui
13
+ from .base import BindableUi, DisableableMixin
14
+
15
+
16
+ class CircularProgressBindableUi(
17
+ BindableUi[ui.circular_progress],
18
+ DisableableMixin,
19
+ ):
20
+ def __init__(
21
+ self,
22
+ value: TMaybeRef[float] = 0.0,
23
+ *,
24
+ min: TMaybeRef[float] = 0.0, # pylint: disable=redefined-builtin
25
+ max: TMaybeRef[float] = 1.0, # pylint: disable=redefined-builtin
26
+ size: Optional[TMaybeRef[str]] = "xl",
27
+ show_value: TMaybeRef[bool] = True,
28
+ color: Optional[TMaybeRef[str]] = "primary",
29
+ ) -> None:
30
+ pc = ParameterClassifier(
31
+ locals(),
32
+ maybeRefs=[
33
+ "value",
34
+ "min",
35
+ "max",
36
+ "color",
37
+ "size",
38
+ "show_value",
39
+ ],
40
+ )
41
+
42
+ value_kws = pc.get_values_kws()
43
+ element = ui.circular_progress(**value_kws)
44
+ super().__init__(element) # type: ignore
45
+
46
+ for key, value in pc.get_bindings().items():
47
+ self.bind_prop(key, value) # type: ignore
48
+
49
+ @property
50
+ def value(self):
51
+ return self.element.value
52
+
53
+ def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
54
+ if prop == "value":
55
+ return self.bind_value(ref_ui)
56
+
57
+ return super().bind_prop(prop, ref_ui)
58
+
59
+ def bind_value(self, ref_ui: ReadonlyRef[float]):
60
+ @ui_effect
61
+ def _():
62
+ self.element.set_value(to_value(ref_ui))
63
+
64
+ return self
@@ -0,0 +1,36 @@
1
+ from typing import (
2
+ Any,
3
+ )
4
+ from ex4nicegui.reactive.utils import ParameterClassifier
5
+ from ex4nicegui.utils.signals import (
6
+ _TMaybeRef as TMaybeRef,
7
+ )
8
+ from nicegui import ui
9
+ from .base import BindableUi
10
+
11
+
12
+ class ColumnBindableUi(BindableUi[ui.column]):
13
+ def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
14
+ pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
15
+ element = ui.column(**pc.get_values_kws())
16
+
17
+ super().__init__(element)
18
+
19
+ for key, value in pc.get_bindings().items():
20
+ self.bind_prop(key, value) # type: ignore
21
+
22
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
23
+ if prop == "wrap":
24
+ return self.bind_wrap(ref_ui)
25
+
26
+ return super().bind_prop(prop, ref_ui)
27
+
28
+ def bind_wrap(self, ref_ui: TMaybeRef):
29
+ self.bind_classes({"wrap": ref_ui})
30
+
31
+ def __enter__(self):
32
+ self.element.__enter__()
33
+ return self
34
+
35
+ def __exit__(self, *_: Any):
36
+ self.element.__exit__(*_)
@@ -0,0 +1,75 @@
1
+ from typing import (
2
+ Any,
3
+ Callable,
4
+ Optional,
5
+ )
6
+ from ex4nicegui.reactive.utils import ParameterClassifier
7
+ from ex4nicegui.utils.apiEffect import ui_effect
8
+
9
+ from ex4nicegui.utils.signals import (
10
+ ReadonlyRef,
11
+ _TMaybeRef as TMaybeRef,
12
+ to_value,
13
+ )
14
+ from nicegui import ui
15
+ from .base import BindableUi, DisableableMixin
16
+
17
+
18
+ class KnobBindableUi(
19
+ BindableUi[ui.knob],
20
+ DisableableMixin,
21
+ ):
22
+ def __init__(
23
+ self,
24
+ value: TMaybeRef[float] = 0.0,
25
+ *,
26
+ min: TMaybeRef[float] = 0.0, # pylint: disable=redefined-builtin
27
+ max: TMaybeRef[float] = 1.0, # pylint: disable=redefined-builtin
28
+ step: TMaybeRef[float] = 0.01,
29
+ color: Optional[TMaybeRef[str]] = "primary",
30
+ center_color: Optional[TMaybeRef[str]] = None,
31
+ track_color: Optional[TMaybeRef[str]] = None,
32
+ size: Optional[TMaybeRef[str]] = None,
33
+ show_value: TMaybeRef[bool] = False,
34
+ on_change: Optional[Callable[..., Any]] = None,
35
+ ) -> None:
36
+ pc = ParameterClassifier(
37
+ locals(),
38
+ maybeRefs=[
39
+ "value",
40
+ "min",
41
+ "max",
42
+ "step",
43
+ "color",
44
+ "center_color",
45
+ "track_color",
46
+ "size",
47
+ "show_value",
48
+ ],
49
+ v_model=("value", "on_change"),
50
+ events=["on_change"],
51
+ )
52
+
53
+ value_kws = pc.get_values_kws()
54
+ element = ui.knob(**value_kws)
55
+ super().__init__(element) # type: ignore
56
+
57
+ for key, value in pc.get_bindings().items():
58
+ self.bind_prop(key, value) # type: ignore
59
+
60
+ @property
61
+ def value(self):
62
+ return self.element.value
63
+
64
+ def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
65
+ if prop == "value":
66
+ return self.bind_value(ref_ui)
67
+
68
+ return super().bind_prop(prop, ref_ui)
69
+
70
+ def bind_value(self, ref_ui: ReadonlyRef[float]):
71
+ @ui_effect
72
+ def _():
73
+ self.element.set_value(to_value(ref_ui))
74
+
75
+ return self
@@ -4,7 +4,6 @@ from typing import (
4
4
  Optional,
5
5
  TypeVar,
6
6
  Dict,
7
- Union,
8
7
  )
9
8
  from ex4nicegui.reactive.utils import ParameterClassifier
10
9
  from ex4nicegui.utils.apiEffect import ui_effect
@@ -26,7 +25,7 @@ class NumberBindableUi(BindableUi[ui.number]):
26
25
  label: Optional[TMaybeRef[str]] = None,
27
26
  *,
28
27
  placeholder: Optional[TMaybeRef[str]] = None,
29
- value: TMaybeRef[Union[float, None]] = None,
28
+ value: Optional[TMaybeRef[float]] = None,
30
29
  min: Optional[TMaybeRef[float]] = None,
31
30
  max: Optional[TMaybeRef[float]] = None,
32
31
  step: Optional[TMaybeRef[float]] = None,
@@ -0,0 +1,35 @@
1
+ from typing import (
2
+ Any,
3
+ )
4
+ from ex4nicegui.reactive.utils import ParameterClassifier
5
+ from ex4nicegui.utils.signals import (
6
+ _TMaybeRef as TMaybeRef,
7
+ )
8
+ from nicegui import ui
9
+ from .base import BindableUi
10
+
11
+
12
+ class RowBindableUi(BindableUi[ui.row]):
13
+ def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
14
+ pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
15
+ element = ui.row(**pc.get_values_kws())
16
+
17
+ super().__init__(element)
18
+ for key, value in pc.get_bindings().items():
19
+ self.bind_prop(key, value) # type: ignore
20
+
21
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
22
+ if prop == "wrap":
23
+ return self.bind_wrap(ref_ui)
24
+
25
+ return super().bind_prop(prop, ref_ui)
26
+
27
+ def bind_wrap(self, ref_ui: TMaybeRef):
28
+ self.bind_classes({"wrap": ref_ui})
29
+
30
+ def __enter__(self):
31
+ self.element.__enter__()
32
+ return self
33
+
34
+ def __exit__(self, *_: Any):
35
+ self.element.__exit__(*_)