pyglove 0.4.5.dev202412080808__tar.gz → 0.4.5.dev202412100720__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 (209) hide show
  1. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/PKG-INFO +1 -1
  2. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/base.py +1 -1
  3. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/base.py +41 -4
  4. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/label.py +12 -1
  5. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/label_test.py +21 -2
  6. pyglove-0.4.5.dev202412100720/pyglove/core/views/html/controls/tab.py +242 -0
  7. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/tab_test.py +13 -15
  8. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove.egg-info/PKG-INFO +1 -1
  9. pyglove-0.4.5.dev202412080808/pyglove/core/views/html/controls/tab.py +0 -169
  10. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/LICENSE +0 -0
  11. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/README.md +0 -0
  12. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/__init__.py +0 -0
  13. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/__init__.py +0 -0
  14. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/detouring/__init__.py +0 -0
  15. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/detouring/class_detour.py +0 -0
  16. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/detouring/class_detour_test.py +0 -0
  17. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/__init__.py +0 -0
  18. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/base.py +0 -0
  19. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/base_test.py +0 -0
  20. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/categorical.py +0 -0
  21. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/categorical_test.py +0 -0
  22. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/custom.py +0 -0
  23. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/custom_test.py +0 -0
  24. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/deduping.py +0 -0
  25. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/deduping_test.py +0 -0
  26. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/dna_generator.py +0 -0
  27. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/dna_generator_test.py +0 -0
  28. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/numerical.py +0 -0
  29. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/numerical_test.py +0 -0
  30. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/random.py +0 -0
  31. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/random_test.py +0 -0
  32. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/space.py +0 -0
  33. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/space_test.py +0 -0
  34. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/sweeping.py +0 -0
  35. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/geno/sweeping_test.py +0 -0
  36. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/__init__.py +0 -0
  37. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/base.py +0 -0
  38. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/categorical.py +0 -0
  39. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/categorical_test.py +0 -0
  40. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/custom.py +0 -0
  41. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/custom_test.py +0 -0
  42. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/derived.py +0 -0
  43. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/derived_test.py +0 -0
  44. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
  45. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
  46. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/evolvable.py +0 -0
  47. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/evolvable_test.py +0 -0
  48. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/iter.py +0 -0
  49. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/iter_test.py +0 -0
  50. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/numerical.py +0 -0
  51. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/numerical_test.py +0 -0
  52. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/object_template.py +0 -0
  53. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/hyper/object_template_test.py +0 -0
  54. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/io/__init__.py +0 -0
  55. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/io/file_system.py +0 -0
  56. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/io/file_system_test.py +0 -0
  57. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/io/sequence.py +0 -0
  58. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/io/sequence_test.py +0 -0
  59. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/logging.py +0 -0
  60. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/logging_test.py +0 -0
  61. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/__init__.py +0 -0
  62. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/codegen.py +0 -0
  63. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/codegen_test.py +0 -0
  64. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/common_traits.py +0 -0
  65. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/common_traits_test.py +0 -0
  66. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/docstr_utils.py +0 -0
  67. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/docstr_utils_test.py +0 -0
  68. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/error_utils.py +0 -0
  69. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/error_utils_test.py +0 -0
  70. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/formatting.py +0 -0
  71. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/formatting_test.py +0 -0
  72. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/hierarchical.py +0 -0
  73. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/hierarchical_test.py +0 -0
  74. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/json_conversion.py +0 -0
  75. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/json_conversion_test.py +0 -0
  76. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/missing.py +0 -0
  77. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/missing_test.py +0 -0
  78. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/thread_local.py +0 -0
  79. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/thread_local_test.py +0 -0
  80. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/timing.py +0 -0
  81. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/timing_test.py +0 -0
  82. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/value_location.py +0 -0
  83. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/object_utils/value_location_test.py +0 -0
  84. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/__init__.py +0 -0
  85. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/object_factory.py +0 -0
  86. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/object_factory_test.py +0 -0
  87. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/pattern_based.py +0 -0
  88. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/pattern_based_test.py +0 -0
  89. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/rule_based.py +0 -0
  90. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/patching/rule_based_test.py +0 -0
  91. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/__init__.py +0 -0
  92. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/base.py +0 -0
  93. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/base_test.py +0 -0
  94. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/boilerplate.py +0 -0
  95. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/boilerplate_test.py +0 -0
  96. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/class_wrapper.py +0 -0
  97. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
  98. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/compounding.py +0 -0
  99. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/compounding_test.py +0 -0
  100. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/dict.py +0 -0
  101. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/dict_test.py +0 -0
  102. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/diff.py +0 -0
  103. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/diff_test.py +0 -0
  104. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/flags.py +0 -0
  105. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/flags_test.py +0 -0
  106. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/functor.py +0 -0
  107. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/functor_test.py +0 -0
  108. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/inferred.py +0 -0
  109. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/inferred_test.py +0 -0
  110. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/list.py +0 -0
  111. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/list_test.py +0 -0
  112. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/object.py +0 -0
  113. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/object_test.py +0 -0
  114. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/origin.py +0 -0
  115. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/origin_test.py +0 -0
  116. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/pure_symbolic.py +0 -0
  117. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/ref.py +0 -0
  118. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/ref_test.py +0 -0
  119. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/symbolize.py +0 -0
  120. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/symbolic/symbolize_test.py +0 -0
  121. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/__init__.py +0 -0
  122. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/backend.py +0 -0
  123. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/backend_test.py +0 -0
  124. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/early_stopping.py +0 -0
  125. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/local_backend.py +0 -0
  126. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/protocols.py +0 -0
  127. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/protocols_test.py +0 -0
  128. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/sample.py +0 -0
  129. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/tuning/sample_test.py +0 -0
  130. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/__init__.py +0 -0
  131. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/annotated.py +0 -0
  132. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/annotated_test.py +0 -0
  133. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/annotation_conversion.py +0 -0
  134. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/annotation_conversion_test.py +0 -0
  135. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/callable_ext.py +0 -0
  136. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/callable_ext_test.py +0 -0
  137. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/callable_signature.py +0 -0
  138. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/callable_signature_test.py +0 -0
  139. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/class_schema.py +0 -0
  140. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/class_schema_test.py +0 -0
  141. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/custom_typing.py +0 -0
  142. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/inspect.py +0 -0
  143. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/inspect_test.py +0 -0
  144. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/key_specs.py +0 -0
  145. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/key_specs_test.py +0 -0
  146. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/pytype_support.py +0 -0
  147. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/type_conversion.py +0 -0
  148. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/type_conversion_test.py +0 -0
  149. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/typed_missing.py +0 -0
  150. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/typed_missing_test.py +0 -0
  151. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/value_specs.py +0 -0
  152. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/typing/value_specs_test.py +0 -0
  153. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/__init__.py +0 -0
  154. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/base.py +0 -0
  155. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/base_test.py +0 -0
  156. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/__init__.py +0 -0
  157. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/base_test.py +0 -0
  158. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/__init__.py +0 -0
  159. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/progress_bar.py +0 -0
  160. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/progress_bar_test.py +0 -0
  161. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/tooltip.py +0 -0
  162. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/controls/tooltip_test.py +0 -0
  163. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/tree_view.py +0 -0
  164. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/core/views/html/tree_view_test.py +0 -0
  165. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/__init__.py +0 -0
  166. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/early_stopping/__init__.py +0 -0
  167. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/early_stopping/base.py +0 -0
  168. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/early_stopping/base_test.py +0 -0
  169. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/early_stopping/step_wise.py +0 -0
  170. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
  171. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/__init__.py +0 -0
  172. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/base.py +0 -0
  173. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/base_test.py +0 -0
  174. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/hill_climb.py +0 -0
  175. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/hill_climb_test.py +0 -0
  176. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/mutators.py +0 -0
  177. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/mutators_test.py +0 -0
  178. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/neat.py +0 -0
  179. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/neat_test.py +0 -0
  180. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/nsga2.py +0 -0
  181. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/nsga2_test.py +0 -0
  182. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/recombinators.py +0 -0
  183. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/recombinators_test.py +0 -0
  184. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/regularized_evolution.py +0 -0
  185. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
  186. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/selectors.py +0 -0
  187. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/selectors_test.py +0 -0
  188. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/where.py +0 -0
  189. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/evolution/where_test.py +0 -0
  190. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/mutfun/__init__.py +0 -0
  191. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/mutfun/base.py +0 -0
  192. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/mutfun/base_test.py +0 -0
  193. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/mutfun/basic_ops.py +0 -0
  194. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
  195. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/__init__.py +0 -0
  196. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/base.py +0 -0
  197. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/base_test.py +0 -0
  198. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/maths.py +0 -0
  199. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/maths_test.py +0 -0
  200. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/randoms.py +0 -0
  201. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/randoms_test.py +0 -0
  202. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/step_wise.py +0 -0
  203. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove/ext/scalars/step_wise_test.py +0 -0
  204. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove.egg-info/SOURCES.txt +0 -0
  205. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove.egg-info/dependency_links.txt +0 -0
  206. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove.egg-info/requires.txt +0 -0
  207. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/pyglove.egg-info/top_level.txt +0 -0
  208. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/setup.cfg +0 -0
  209. {pyglove-0.4.5.dev202412080808 → pyglove-0.4.5.dev202412100720}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202412080808
3
+ Version: 0.4.5.dev202412100720
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -352,7 +352,7 @@ class Html(base.Content):
352
352
  cls,
353
353
  s: WritableTypes,
354
354
  javascript_str: bool = False
355
- ) -> Union[str, 'Html', None]:
355
+ ) -> Any:
356
356
  """Escapes an HTML writable object."""
357
357
  if s is None:
358
358
  return None
@@ -62,6 +62,7 @@ class HtmlControl(pg_object.Object):
62
62
  super()._on_bound()
63
63
  self._rendered = False
64
64
  self._css_styles = []
65
+ self._dynamic_injected_css = set()
65
66
  self._scripts = []
66
67
 
67
68
  def add_style(self, *css: str) -> 'HtmlControl':
@@ -76,6 +77,7 @@ class HtmlControl(pg_object.Object):
76
77
  def to_html(self, **kwargs) -> Html:
77
78
  """Returns the HTML representation of the control."""
78
79
  self._rendered = True
80
+ self._dynamic_injected_css = set()
79
81
  html = self._to_html(**kwargs)
80
82
  return html.add_style(*self._css_styles).add_script(*self._scripts)
81
83
 
@@ -102,7 +104,7 @@ class HtmlControl(pg_object.Object):
102
104
  """Synchronizes displayed values to members."""
103
105
  self.rebind(fields, skip_notification=True, raise_on_no_change=False)
104
106
 
105
- def _run_javascript(self, code: str) -> None:
107
+ def _run_javascript(self, code: str, debug: bool = False) -> None:
106
108
  """Runs the given JavaScript code."""
107
109
  if not self.interactive:
108
110
  raise ValueError(
@@ -113,6 +115,8 @@ class HtmlControl(pg_object.Object):
113
115
  return
114
116
 
115
117
  code = inspect.cleandoc(code)
118
+ if debug:
119
+ print('RUN JAVSCRIPT:\n', code)
116
120
  if _notebook is not None:
117
121
  _notebook.display(_notebook.Javascript(code))
118
122
 
@@ -121,6 +125,40 @@ class HtmlControl(pg_object.Object):
121
125
  for tracked in all_tracked:
122
126
  tracked.append(code)
123
127
 
128
+ def _add_css_rules(self, css: str) -> None:
129
+ if not self._rendered or not css or css in self._dynamic_injected_css:
130
+ return
131
+ self._run_javascript(
132
+ f"""
133
+ const style = document.createElement('style');
134
+ style.type = 'text/css';
135
+ style.textContent = "{Html.escape(css, javascript_str=True)}";
136
+ document.head.appendChild(style);
137
+ """
138
+ )
139
+ self._dynamic_injected_css.add(css)
140
+
141
+ def _apply_css_rules(self, html: Html) -> None:
142
+ self._add_css_rules(html.styles.content)
143
+
144
+ def _insert_adjacent_html(
145
+ self,
146
+ element_selector_js: str,
147
+ html: Html,
148
+ var_name: str = 'elem',
149
+ position: str = 'beforeend'
150
+ ):
151
+ self._run_javascript(
152
+ f"""
153
+ {element_selector_js}
154
+ {var_name}.insertAdjacentHTML(
155
+ "{position}",
156
+ "{Html.escape(html, javascript_str=True).to_str(content_only=True)}"
157
+ );
158
+ """,
159
+ )
160
+ self._apply_css_rules(html)
161
+
124
162
  def element_id(self, child: Optional[str] = None) -> Optional[str]:
125
163
  """Returns the element id of this control or a child."""
126
164
  if self.id is not None:
@@ -163,14 +201,13 @@ class HtmlControl(pg_object.Object):
163
201
  child: Optional[str] = None,
164
202
  ) -> base.Html:
165
203
  """Updates the inner HTML of the control."""
166
- js_html = Html.escape(html, javascript_str=True)
167
- assert isinstance(js_html, Html), js_html
168
204
  self._run_javascript(
169
205
  f"""
170
206
  elem = document.getElementById("{self.element_id(child)}");
171
- elem.innerHTML = "{js_html.to_str(content_only=True)}";
207
+ elem.innerHTML = "{Html.escape(html, javascript_str=True).to_str(content_only=True)}";
172
208
  """
173
209
  )
210
+ self._add_css_rules(html.styles.content)
174
211
  return html
175
212
 
176
213
  def _update_style(
@@ -96,6 +96,8 @@ class Label(HtmlControl):
96
96
  tooltip: Union[str, Html, None] = None,
97
97
  link: Optional[str] = None,
98
98
  styles: Optional[Dict[str, Any]] = None,
99
+ add_class: Optional[List[str]] = None,
100
+ remove_class: Optional[List[str]] = None,
99
101
  ) -> None:
100
102
  if text is not None:
101
103
  self._sync_members(text=self._update_content(text))
@@ -105,7 +107,16 @@ class Label(HtmlControl):
105
107
  self._sync_members(link=self._update_property('href', link))
106
108
  if tooltip is not None:
107
109
  self.tooltip.update(content=tooltip)
108
-
110
+ if add_class or remove_class:
111
+ css_classes = list(self.css_classes)
112
+ for x in add_class or []:
113
+ self._add_css_class(x)
114
+ css_classes.append(x)
115
+ for x in remove_class or []:
116
+ self._remove_css_class(x)
117
+ if x in css_classes:
118
+ css_classes.remove(x)
119
+ self._sync_members(css_classes=css_classes)
109
120
 
110
121
  # Register converter for automatic conversion.
111
122
  pg_typing.register_converter(str, Label, Label)
@@ -60,19 +60,26 @@ class LabelTest(TestCase):
60
60
  )
61
61
 
62
62
  def test_update(self):
63
- label = label_lib.Label('foo', 'bar', 'http://google.com', interactive=True)
63
+ label = label_lib.Label(
64
+ 'foo', 'bar', 'http://google.com',
65
+ interactive=True,
66
+ css_classes=['foo', 'bar'],
67
+ )
64
68
  self.assertIn('id="control-', label.to_html_str(content_only=True))
65
69
  with label.track_scripts() as scripts:
66
70
  label.update(
67
71
  'bar',
68
72
  tooltip='baz',
69
73
  link='http://www.yahoo.com',
70
- styles=dict(color='red')
74
+ styles=dict(color='red'),
75
+ add_class=['baz'],
76
+ remove_class=['bar'],
71
77
  )
72
78
  self.assertEqual(label.text, 'bar')
73
79
  self.assertEqual(label.tooltip.content, 'baz')
74
80
  self.assertEqual(label.link, 'http://www.yahoo.com')
75
81
  self.assertEqual(label.styles, dict(color='red'))
82
+ self.assertEqual(label.css_classes, ['foo', 'baz'])
76
83
  self.assertEqual(
77
84
  scripts,
78
85
  [
@@ -106,6 +113,18 @@ class LabelTest(TestCase):
106
113
  elem.classList.remove("html-content");
107
114
  """
108
115
  ),
116
+ inspect.cleandoc(
117
+ f"""
118
+ elem = document.getElementById("{label.element_id()}");
119
+ elem.classList.add("baz");
120
+ """
121
+ ),
122
+ inspect.cleandoc(
123
+ f"""
124
+ elem = document.getElementById("{label.element_id()}");
125
+ elem.classList.remove("bar");
126
+ """
127
+ ),
109
128
  ]
110
129
  )
111
130
 
@@ -0,0 +1,242 @@
1
+ # Copyright 2024 The PyGlove Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Tab control."""
15
+
16
+ from typing import Annotated, List, Literal, Union
17
+
18
+ from pyglove.core.symbolic import flags as pg_flags
19
+ from pyglove.core.symbolic import object as pg_object
20
+
21
+ # pylint: disable=g-importing-member
22
+ from pyglove.core.views.html.base import Html
23
+ from pyglove.core.views.html.base import HtmlConvertible
24
+ from pyglove.core.views.html.controls.base import HtmlControl
25
+ from pyglove.core.views.html.controls.label import Label
26
+ # pylint: enable=g-importing-member
27
+
28
+
29
+ class Tab(pg_object.Object):
30
+ """A tab."""
31
+
32
+ label: Annotated[
33
+ Label,
34
+ 'The label of the tab.'
35
+ ]
36
+
37
+ content: Annotated[
38
+ Union[Html, HtmlConvertible],
39
+ 'The content of the tab.'
40
+ ]
41
+
42
+ css_classes: Annotated[
43
+ List[str],
44
+ 'The CSS classes of the tab.'
45
+ ] = []
46
+
47
+
48
+ @pg_object.use_init_args(
49
+ ['tabs', 'selected', 'tab_position', 'id', 'css_classes', 'styles']
50
+ )
51
+ class TabControl(HtmlControl):
52
+ """A tab control."""
53
+
54
+ tabs: Annotated[
55
+ List[Tab],
56
+ 'The tabs of the tab control.'
57
+ ]
58
+
59
+ selected: Annotated[
60
+ int,
61
+ 'The index of the selected tab.'
62
+ ] = 0
63
+
64
+ tab_position: Annotated[
65
+ Literal['top', 'left'],
66
+ 'The direction of the tab control.'
67
+ ] = 'top'
68
+
69
+ interactive = True
70
+
71
+ def append(self, tab: Tab) -> None:
72
+ with pg_flags.notify_on_change(False):
73
+ self.tabs.append(tab)
74
+
75
+ self._insert_adjacent_html(
76
+ f"""
77
+ const elem = document.getElementById('{self.element_id()}-button-group');
78
+ """,
79
+ self._tab_button(tab, len(self.tabs) - 1),
80
+ position='beforeend',
81
+ )
82
+ self._insert_adjacent_html(
83
+ f"""
84
+ const elem = document.getElementById('{self.element_id()}-content-group');
85
+ """,
86
+ self._tab_content(tab, len(self.tabs) - 1),
87
+ position='beforeend',
88
+ )
89
+
90
+ def extend(self, tabs: List[Tab]) -> None:
91
+ for tab in tabs:
92
+ self.append(tab)
93
+
94
+ def _to_html(self, **kwargs):
95
+ return Html.element(
96
+ 'table',
97
+ [
98
+ '<tr><td>',
99
+ Html.element(
100
+ 'div',
101
+ [self._tab_button(tab, i) for i, tab in enumerate(self.tabs)],
102
+ css_classes=[
103
+ 'tab-button-group',
104
+ self.tab_position
105
+ ] + self.css_classes,
106
+ id=self.element_id('button-group'),
107
+ ),
108
+ ('</td><td>' if self.tab_position == 'left'
109
+ else '</td></tr><tr><td>'),
110
+ Html.element(
111
+ 'div',
112
+ [self._tab_content(tab, i) for i, tab in enumerate(self.tabs)],
113
+ css_classes=[
114
+ 'tab-content-group',
115
+ self.tab_position
116
+ ] + self.css_classes,
117
+ id=self.element_id('content-group'),
118
+ ),
119
+ '</td></tr>'
120
+ ],
121
+ css_classes=['tab-control'],
122
+ styles=self.styles,
123
+ ).add_script(
124
+ """
125
+ function openTab(event, controlId, tabId) {
126
+ const tabButtons = document.querySelectorAll('#' + controlId + '-button-group > .tab-button');
127
+ for (let i = 0; i < tabButtons.length; i++) {
128
+ tabButtons[i].classList.remove('selected');
129
+ }
130
+ const tabContents = document.querySelectorAll('#' + controlId + '-content-group > .tab-content');
131
+ for (let i = 0; i < tabContents.length; i++) {
132
+ tabContents[i].classList.remove('selected')
133
+ }
134
+ const tabButton = event.currentTarget;
135
+ tabButton.classList.add('selected');
136
+ document.getElementById(tabId).classList.add('selected');
137
+ }
138
+ """
139
+ ).add_style(
140
+ """
141
+ .tab-control {
142
+ border-spacing: 0px;
143
+ border-collapse: collapse;
144
+ height: 100%;
145
+ margin-top: 10px;
146
+ }
147
+ .tab-control td {
148
+ padding: 0px;
149
+ margin: 0px;
150
+ vertical-align: top;
151
+ }
152
+ .top.tab-button-group {
153
+ border-left: 1px solid #DDD;
154
+ border-top: 1px solid #DDD;
155
+ border-right: 1px solid #DDD;
156
+ border-radius: 5px 5px 0px 0px;
157
+ padding: 0px 5px 0px 0px;
158
+ margin-bottom: -2px;
159
+ }
160
+ .tab-button {
161
+ background-color: transparent;
162
+ border-radius: 5px;
163
+ border: 0px;
164
+ font-weight: bold;
165
+ color: gray;
166
+ outline: none;
167
+ cursor: pointer;
168
+ transition: 0.3s;
169
+ }
170
+ .tab-button:hover {
171
+ background-color: #fff1dd;
172
+ }
173
+ .tab-button.selected {
174
+ background-color: #f0ecf9;
175
+ color: black;
176
+ }
177
+ .top.tab-content-group {
178
+ border-left: 1px solid #DDD;
179
+ border-right: 1px solid #DDD;
180
+ border-bottom: 1px solid #DDD;
181
+ border-radius: 0px 0px 5px 5px;
182
+ margin: 0px;
183
+ padding: 5px;
184
+ height: 100%;
185
+ }
186
+ .top > .tab-button {
187
+ margin: 5px 0px 5px 5px;
188
+ }
189
+ .tab-content {
190
+ display: none;
191
+ }
192
+ .tab-content.selected {
193
+ display: block;
194
+ }
195
+ .left.tab-button-group {
196
+ display: inline-flex;
197
+ flex-direction: column;
198
+ border: 1px solid #DDD;
199
+ border-radius: 5px;
200
+ margin-right: 5px;
201
+ padding: 0px 0px 5px 0px;
202
+ }
203
+ .left.tab-content-group {
204
+ border: 0px
205
+ margin: 0px;
206
+ padding: 0px;
207
+ height: 100%;
208
+ }
209
+ .left > .tab-button {
210
+ text-align: left;
211
+ margin: 5px 5px 0px 5px;
212
+ }
213
+ """
214
+ )
215
+
216
+ def _tab_button(self, tab: Tab, i: int) -> Html:
217
+ return Html.element(
218
+ 'button',
219
+ [
220
+ tab.label
221
+ ],
222
+ css_classes=[
223
+ 'tab-button',
224
+ 'selected' if i == self.selected else None
225
+ ] + tab.css_classes,
226
+ onclick=(
227
+ f"""openTab(event, '{self.element_id()}', '{self.element_id(str(i))}')"""
228
+ )
229
+ )
230
+
231
+ def _tab_content(self, tab: Tab, i: int) -> Html:
232
+ return Html.element(
233
+ 'div',
234
+ [
235
+ tab.content
236
+ ],
237
+ css_classes=[
238
+ 'tab-content',
239
+ 'selected' if i == self.selected else None
240
+ ] + tab.css_classes,
241
+ id=self.element_id(str(i))
242
+ )
@@ -30,26 +30,24 @@ class TabControlTest(unittest.TestCase):
30
30
 
31
31
  def test_basic(self):
32
32
  tab = tab_lib.TabControl([
33
- tab_lib.Tab('foo', base.Html('<h1>foo</h1>')),
33
+ tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), css_classes=['foo']),
34
34
  tab_lib.Tab('bar', base.Html('<h1>bar</h1>')),
35
35
  ])
36
+ elem_id = tab.element_id()
36
37
  self.assert_html_content(
37
38
  tab,
38
- (
39
- f'<div class="tab-control top" id="{tab.element_id()}">'
40
- '<div class="tab-button-group"><button class="tab-button selected" '
41
- f'''onclick="openTab(event, '{tab.element_id()}', '''
42
- f''''{tab.element_id(str(0))}')">'''
43
- '<a class="label">foo</a></button><button class="tab-button" '
44
- f'''onclick="openTab(event, '{tab.element_id()}', '''
45
- f''''{tab.element_id(str(1))}')">'''
46
- '<a class="label">bar</a></button></div>'
47
- '<div class="tab-content" style="display:block;" '
48
- f'id="{tab.element_id(str(0))}"><h1>foo</h1></div>'
49
- '<div class="tab-content" style="display:none;" '
50
- f'id="{tab.element_id(str(1))}"><h1>bar</h1></div></div>'
51
- )
39
+ f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button selected foo" onclick="openTab(event, '{elem_id}', '{elem_id}-0')"><a class="label">foo</a></button><button class="tab-button" onclick="openTab(event, '{elem_id}', '{elem_id}-1')"><a class="label">bar</a></button></div></td></tr><tr><td><div class="tab-content-group top" id="{elem_id}-content-group"><div class="tab-content selected foo" id="{elem_id}-0"><h1>foo</h1></div><div class="tab-content" id="{elem_id}-1"><h1>bar</h1></div></div></td></tr></table>"""
52
40
  )
41
+ with tab.track_scripts() as scripts:
42
+ tab.extend([
43
+ tab_lib.Tab(
44
+ 'baz',
45
+ base.Html(
46
+ '<h1 class="a">baz</h1>').add_style('.a { color: red; }')
47
+ ),
48
+ tab_lib.Tab('qux', base.Html('<h1>qux</h1>')),
49
+ ])
50
+ self.assertEqual(len(scripts), 6)
53
51
 
54
52
 
55
53
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202412080808
3
+ Version: 0.4.5.dev202412100720
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -1,169 +0,0 @@
1
- # Copyright 2024 The PyGlove Authors
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- """Tab control."""
15
-
16
- from typing import Annotated, List, Literal, Union
17
-
18
- from pyglove.core.symbolic import object as pg_object
19
- # pylint: disable=g-importing-member
20
- from pyglove.core.views.html.base import Html
21
- from pyglove.core.views.html.base import HtmlConvertible
22
- from pyglove.core.views.html.controls.base import HtmlControl
23
- from pyglove.core.views.html.controls.label import Label
24
- # pylint: enable=g-importing-member
25
-
26
-
27
- class Tab(pg_object.Object):
28
- """A tab."""
29
-
30
- label: Annotated[
31
- Label,
32
- 'The label of the tab.'
33
- ]
34
-
35
- content: Annotated[
36
- Union[Html, HtmlConvertible],
37
- 'The content of the tab.'
38
- ]
39
-
40
-
41
- @pg_object.use_init_args(
42
- ['tabs', 'selected', 'tab_position', 'id', 'css_classes', 'styles']
43
- )
44
- class TabControl(HtmlControl):
45
- """A tab control."""
46
-
47
- tabs: Annotated[
48
- List[Tab],
49
- 'The tabs of the tab control.'
50
- ]
51
-
52
- selected: Annotated[
53
- int,
54
- 'The index of the selected tab.'
55
- ] = 0
56
-
57
- tab_position: Annotated[
58
- Literal['top', 'left'],
59
- 'The direction of the tab control.'
60
- ] = 'top'
61
-
62
- interactive = True
63
-
64
- def _to_html(self, **kwargs):
65
- return Html.element(
66
- 'div',
67
- [
68
- Html.element(
69
- 'div',
70
- [
71
- Html.element(
72
- 'button',
73
- [
74
- tab.label
75
- ],
76
- css_classes=[
77
- 'tab-button',
78
- 'selected' if i == self.selected else None
79
- ],
80
- onclick=(
81
- f"""openTab(event, '{self.element_id()}', '{self.element_id(str(i))}')"""
82
- )
83
- ) for i, tab in enumerate(self.tabs)
84
- ],
85
- css_classes=['tab-button-group'],
86
- ),
87
- ] + [
88
- Html.element(
89
- 'div',
90
- [
91
- tab.content
92
- ],
93
- css_classes=['tab-content'],
94
- styles=dict(
95
- display='block' if i == self.selected else 'none'
96
- ),
97
- id=self.element_id(str(i))
98
- ) for i, tab in enumerate(self.tabs)
99
- ],
100
- css_classes=['tab-control', self.tab_position] + self.css_classes,
101
- id=self.element_id(),
102
- styles=self.styles,
103
- ).add_script(
104
- """
105
- function openTab(event, controlId, tabId) {
106
- const tabButtons = document.querySelectorAll('#' + controlId + '> .tab-button-group > .tab-button');
107
- for (let i = 0; i < tabButtons.length; i++) {
108
- tabButtons[i].classList.remove('selected');
109
- }
110
- const tabContents = document.querySelectorAll('#' + controlId + '> .tab-content');
111
- for (let i = 0; i < tabContents.length; i++) {
112
- tabContents[i].style.display = 'none';
113
- }
114
- const tabButton = event.currentTarget;
115
- tabButton.classList.add('selected');
116
- document.getElementById(tabId).style.display = "block";
117
- }
118
- """
119
- ).add_style(
120
- """
121
- .top .tab-button-group {
122
- overflow-x: hidden;
123
- border-bottom: 1px solid #EEE;
124
- }
125
- .left .tab-button-group {
126
- float: left;
127
- top: 0;
128
- }
129
- .tab-button {
130
- background-color: #EEE;
131
- border: 1px solid #EEE;
132
- outline: none;
133
- cursor: pointer;
134
- transition: 0.3s;
135
- padding: 10px 15px 10px 15px;
136
- }
137
- .top .tab-button {
138
- border-top-width: 2px;
139
- }
140
- .left .tab-button {
141
- display: block;
142
- width: 100%;
143
- border-left-width: 2px;
144
- }
145
- .top .tab-button.selected {
146
- border-bottom-color: #fff;
147
- border-top-color: #B721FF;
148
- background: #fff;
149
- }
150
- .left .tab-button.selected {
151
- border-right-color: #fff;
152
- border-left-color: #B721FF;
153
- background: #fff;
154
- }
155
- .top .tab-button:hover {
156
- border-top-color: orange;
157
- }
158
- .left .tab-button:hover {
159
- border-left-color: orange;
160
- }
161
- .tab-content {
162
- display: none;
163
- padding: 10px;
164
- }
165
- .left .tab-content {
166
- float: left;
167
- }
168
- """
169
- )