playbook_ui 16.4.0 → 16.5.0.pre.alpha.RTEPOC15682

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +5 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +9 -11
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +65 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +3 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +2 -2
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +9 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_grouped_headers_composition.jsx +235 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_grouped_headers_composition.md +17 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_rails.html.erb +57 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_rails.md +7 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_grouped_headers_composition_mock_data.json +98 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/index.js +82 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/kit.schema.json +278 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/scss_partials/advanced_table_sticky_mixin.scss +6 -2
  18. data/app/pb_kits/playbook/pb_advanced_table/table_body.html.erb +21 -4
  19. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +115 -9
  20. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -1
  21. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +33 -0
  22. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +3 -1
  23. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +12 -1
  24. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +4 -1
  25. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb +9 -1
  26. data/app/pb_kits/playbook/pb_avatar/kit.schema.json +86 -0
  27. data/app/pb_kits/playbook/pb_background/kit.schema.json +208 -0
  28. data/app/pb_kits/playbook/pb_badge/kit.schema.json +73 -0
  29. data/app/pb_kits/playbook/pb_body/kit.schema.json +101 -0
  30. data/app/pb_kits/playbook/pb_bread_crumbs/kit.schema.json +28 -0
  31. data/app/pb_kits/playbook/pb_button/docs/_button_full_width_rails.md +19 -0
  32. data/app/pb_kits/playbook/pb_button/docs/_button_full_width_react.md +23 -0
  33. data/app/pb_kits/playbook/pb_button/docs/{_button_loading.html.erb → _button_loading_rails.html.erb} +5 -5
  34. data/app/pb_kits/playbook/pb_button/docs/_button_loading_rails.md +3 -0
  35. data/app/pb_kits/playbook/pb_button/docs/example.yml +1 -1
  36. data/app/pb_kits/playbook/pb_button/kit.schema.json +207 -0
  37. data/app/pb_kits/playbook/pb_button_toolbar/kit.schema.json +67 -0
  38. data/app/pb_kits/playbook/pb_caption/kit.schema.json +91 -0
  39. data/app/pb_kits/playbook/pb_card/kit.schema.json +168 -0
  40. data/app/pb_kits/playbook/pb_checkbox/kit.schema.json +143 -0
  41. data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.scss +5 -0
  42. data/app/pb_kits/playbook/pb_circle_icon_button/kit.schema.json +117 -0
  43. data/app/pb_kits/playbook/pb_collapsible/kit.schema.json +90 -0
  44. data/app/pb_kits/playbook/pb_contact/kit.schema.json +59 -0
  45. data/app/pb_kits/playbook/pb_copy_button/kit.schema.json +94 -0
  46. data/app/pb_kits/playbook/pb_currency/kit.schema.json +142 -0
  47. data/app/pb_kits/playbook/pb_dashboard_value/kit.schema.json +54 -0
  48. data/app/pb_kits/playbook/pb_date/kit.schema.json +98 -0
  49. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +35 -0
  50. data/app/pb_kits/playbook/pb_date_picker/kit.schema.json +375 -0
  51. data/app/pb_kits/playbook/pb_date_range_inline/kit.schema.json +76 -0
  52. data/app/pb_kits/playbook/pb_date_range_stacked/kit.schema.json +36 -0
  53. data/app/pb_kits/playbook/pb_date_stacked/kit.schema.json +78 -0
  54. data/app/pb_kits/playbook/pb_date_time/kit.schema.json +96 -0
  55. data/app/pb_kits/playbook/pb_date_time_stacked/kit.schema.json +62 -0
  56. data/app/pb_kits/playbook/pb_date_year_stacked/kit.schema.json +42 -0
  57. data/app/pb_kits/playbook/pb_detail/kit.schema.json +72 -0
  58. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.html.erb +9 -18
  59. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.jsx +5 -24
  60. data/app/pb_kits/playbook/pb_dialog/kit.schema.json +186 -0
  61. data/app/pb_kits/playbook/pb_distribution_bar/kit.schema.json +48 -0
  62. data/app/pb_kits/playbook/pb_draggable/kit.schema.json +60 -0
  63. data/app/pb_kits/playbook/pb_dropdown/kit.schema.json +244 -0
  64. data/app/pb_kits/playbook/pb_empty_state/kit.schema.json +117 -0
  65. data/app/pb_kits/playbook/pb_file_upload/kit.schema.json +124 -0
  66. data/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +3 -1
  67. data/app/pb_kits/playbook/pb_filter/filter.rb +1 -1
  68. data/app/pb_kits/playbook/pb_filter/kit.schema.json +87 -0
  69. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/kit.schema.json +118 -0
  70. data/app/pb_kits/playbook/pb_flex/kit.schema.json +151 -0
  71. data/app/pb_kits/playbook/pb_form/kit.schema.json +53 -0
  72. data/app/pb_kits/playbook/pb_form_group/kit.schema.json +30 -0
  73. data/app/pb_kits/playbook/pb_form_pill/kit.schema.json +132 -0
  74. data/app/pb_kits/playbook/pb_hashtag/kit.schema.json +71 -0
  75. data/app/pb_kits/playbook/pb_highlight/kit.schema.json +35 -0
  76. data/app/pb_kits/playbook/pb_home_address_street/kit.schema.json +121 -0
  77. data/app/pb_kits/playbook/pb_icon/icon.rb +7 -1
  78. data/app/pb_kits/playbook/pb_icon/kit.schema.json +180 -0
  79. data/app/pb_kits/playbook/pb_icon_button/kit.schema.json +116 -0
  80. data/app/pb_kits/playbook/pb_icon_circle/kit.schema.json +65 -0
  81. data/app/pb_kits/playbook/pb_icon_stat_value/kit.schema.json +96 -0
  82. data/app/pb_kits/playbook/pb_icon_value/kit.schema.json +49 -0
  83. data/app/pb_kits/playbook/pb_image/kit.schema.json +74 -0
  84. data/app/pb_kits/playbook/pb_label_pill/kit.schema.json +52 -0
  85. data/app/pb_kits/playbook/pb_label_value/kit.schema.json +84 -0
  86. data/app/pb_kits/playbook/pb_layout/kit.schema.json +107 -0
  87. data/app/pb_kits/playbook/pb_legend/kit.schema.json +44 -0
  88. data/app/pb_kits/playbook/pb_link/kit.schema.json +118 -0
  89. data/app/pb_kits/playbook/pb_list/_list_mixin.scss +4 -4
  90. data/app/pb_kits/playbook/pb_list/kit.schema.json +110 -0
  91. data/app/pb_kits/playbook/pb_loading_inline/kit.schema.json +55 -0
  92. data/app/pb_kits/playbook/pb_map/kit.schema.json +51 -0
  93. data/app/pb_kits/playbook/pb_message/kit.schema.json +104 -0
  94. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +3 -1
  95. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.html.erb +109 -0
  96. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.jsx +127 -0
  97. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.md +1 -0
  98. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -0
  99. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  100. data/app/pb_kits/playbook/pb_multi_level_select/kit.schema.json +190 -0
  101. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +3 -0
  102. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +27 -0
  103. data/app/pb_kits/playbook/pb_multiple_users/kit.schema.json +67 -0
  104. data/app/pb_kits/playbook/pb_multiple_users_stacked/kit.schema.json +55 -0
  105. data/app/pb_kits/playbook/pb_nav/kit.schema.json +104 -0
  106. data/app/pb_kits/playbook/pb_online_status/kit.schema.json +62 -0
  107. data/app/pb_kits/playbook/pb_overlay/kit.schema.json +99 -0
  108. data/app/pb_kits/playbook/pb_pagination/kit.schema.json +56 -0
  109. data/app/pb_kits/playbook/pb_passphrase/kit.schema.json +94 -0
  110. data/app/pb_kits/playbook/pb_pb_bar_graph/kit.schema.json +29 -0
  111. data/app/pb_kits/playbook/pb_pb_circle_chart/kit.schema.json +29 -0
  112. data/app/pb_kits/playbook/pb_pb_gauge_chart/kit.schema.json +29 -0
  113. data/app/pb_kits/playbook/pb_pb_line_graph/kit.schema.json +29 -0
  114. data/app/pb_kits/playbook/pb_person/kit.schema.json +36 -0
  115. data/app/pb_kits/playbook/pb_person_contact/kit.schema.json +43 -0
  116. data/app/pb_kits/playbook/pb_phone_number_input/kit.schema.json +157 -0
  117. data/app/pb_kits/playbook/pb_pill/kit.schema.json +57 -0
  118. data/app/pb_kits/playbook/pb_popover/kit.schema.json +82 -0
  119. data/app/pb_kits/playbook/pb_progress_pills/kit.schema.json +60 -0
  120. data/app/pb_kits/playbook/pb_progress_simple/kit.schema.json +78 -0
  121. data/app/pb_kits/playbook/pb_progress_step/kit.schema.json +74 -0
  122. data/app/pb_kits/playbook/pb_radio/kit.schema.json +104 -0
  123. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +1 -1
  124. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +9 -0
  125. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.html.erb +1 -0
  126. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +1 -0
  127. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +1 -0
  128. data/app/pb_kits/playbook/pb_rich_text_editor/kit.schema.json +158 -0
  129. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +303 -0
  130. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +45 -0
  131. data/app/pb_kits/playbook/pb_section_separator/kit.schema.json +77 -0
  132. data/app/pb_kits/playbook/pb_select/docs/_select_attributes.html.erb +1 -0
  133. data/app/pb_kits/playbook/pb_select/docs/_select_blank.html.erb +1 -0
  134. data/app/pb_kits/playbook/pb_select/docs/_select_custom_select.html.erb +1 -1
  135. data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.html.erb +1 -1
  136. data/app/pb_kits/playbook/pb_select/docs/_select_default.html.erb +1 -0
  137. data/app/pb_kits/playbook/pb_select/docs/_select_disabled.html.erb +1 -0
  138. data/app/pb_kits/playbook/pb_select/docs/_select_disabled_options.html.erb +1 -0
  139. data/app/pb_kits/playbook/pb_select/docs/_select_error.html.erb +1 -0
  140. data/app/pb_kits/playbook/pb_select/docs/_select_inline.html.erb +1 -0
  141. data/app/pb_kits/playbook/pb_select/docs/_select_inline_compact.html.erb +1 -0
  142. data/app/pb_kits/playbook/pb_select/docs/_select_inline_show_arrow.html.erb +1 -0
  143. data/app/pb_kits/playbook/pb_select/docs/_select_multiple.html.erb +1 -0
  144. data/app/pb_kits/playbook/pb_select/docs/_select_required.html.erb +1 -0
  145. data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.html.erb +1 -0
  146. data/app/pb_kits/playbook/pb_select/docs/_select_value_text_same.html.erb +1 -0
  147. data/app/pb_kits/playbook/pb_select/kit.schema.json +157 -0
  148. data/app/pb_kits/playbook/pb_select/select.html.erb +5 -5
  149. data/app/pb_kits/playbook/pb_select/select.rb +7 -2
  150. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +39 -0
  151. data/app/pb_kits/playbook/pb_selectable_card/kit.schema.json +116 -0
  152. data/app/pb_kits/playbook/pb_selectable_card_icon/kit.schema.json +116 -0
  153. data/app/pb_kits/playbook/pb_selectable_icon/kit.schema.json +103 -0
  154. data/app/pb_kits/playbook/pb_selectable_list/_selectable_list.scss +19 -1
  155. data/app/pb_kits/playbook/pb_selectable_list/kit.schema.json +54 -0
  156. data/app/pb_kits/playbook/pb_skeleton_loading/kit.schema.json +42 -0
  157. data/app/pb_kits/playbook/pb_source/kit.schema.json +61 -0
  158. data/app/pb_kits/playbook/pb_star_rating/kit.schema.json +152 -0
  159. data/app/pb_kits/playbook/pb_stat_change/kit.schema.json +49 -0
  160. data/app/pb_kits/playbook/pb_stat_value/kit.schema.json +42 -0
  161. data/app/pb_kits/playbook/pb_table/docs/_sections.yml +1 -0
  162. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.html.erb +45 -0
  163. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.md +39 -0
  164. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +2 -1
  165. data/app/pb_kits/playbook/pb_table/docs/example.yml +1 -0
  166. data/app/pb_kits/playbook/pb_table/kit.schema.json +238 -0
  167. data/app/pb_kits/playbook/pb_table/table.html.erb +5 -2
  168. data/app/pb_kits/playbook/pb_table/table.rb +4 -0
  169. data/app/pb_kits/playbook/pb_text_input/kit.schema.json +155 -0
  170. data/app/pb_kits/playbook/pb_textarea/kit.schema.json +163 -0
  171. data/app/pb_kits/playbook/pb_time/kit.schema.json +96 -0
  172. data/app/pb_kits/playbook/pb_time_picker/kit.schema.json +143 -0
  173. data/app/pb_kits/playbook/pb_time_range_inline/kit.schema.json +77 -0
  174. data/app/pb_kits/playbook/pb_time_stacked/kit.schema.json +60 -0
  175. data/app/pb_kits/playbook/pb_timeline/kit.schema.json +57 -0
  176. data/app/pb_kits/playbook/pb_timestamp/kit.schema.json +126 -0
  177. data/app/pb_kits/playbook/pb_title/kit.schema.json +106 -0
  178. data/app/pb_kits/playbook/pb_title_count/kit.schema.json +61 -0
  179. data/app/pb_kits/playbook/pb_title_detail/kit.schema.json +49 -0
  180. data/app/pb_kits/playbook/pb_toggle/kit.schema.json +76 -0
  181. data/app/pb_kits/playbook/pb_tooltip/kit.schema.json +110 -0
  182. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -4
  183. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_createable.html.erb +29 -0
  184. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_createable.md +1 -0
  185. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.jsx +20 -8
  186. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.md +3 -0
  187. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +1 -0
  188. data/app/pb_kits/playbook/pb_typeahead/kit.schema.json +281 -0
  189. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -1
  190. data/app/pb_kits/playbook/pb_user/kit.schema.json +147 -0
  191. data/app/pb_kits/playbook/pb_user_badge/kit.schema.json +47 -0
  192. data/app/pb_kits/playbook/pb_weekday_stacked/kit.schema.json +63 -0
  193. data/app/pb_kits/playbook/utilities/_hover.scss +6 -3
  194. data/app/pb_kits/playbook/utilities/global-props.schema.json +759 -0
  195. data/dist/chunks/{_pb_line_graph-DHY2Tea7.js → _pb_line_graph-EzcacMw_.js} +1 -1
  196. data/dist/chunks/_typeahead-BYUXg9ZT.js +1 -0
  197. data/dist/chunks/{globalProps-C893Xh66.js → globalProps-6Hsm3bJX.js} +1 -1
  198. data/dist/chunks/lib-o4u12zAw.js +29 -0
  199. data/dist/chunks/vendor.js +4 -4
  200. data/dist/menu.yml +77 -40
  201. data/dist/playbook-rails-react-bindings.js +1 -1
  202. data/dist/playbook-rails.js +1 -1
  203. data/dist/playbook.css +1 -1
  204. data/lib/playbook/align_content.rb +17 -28
  205. data/lib/playbook/align_items.rb +17 -28
  206. data/lib/playbook/align_self.rb +17 -28
  207. data/lib/playbook/border_radius.rb +7 -10
  208. data/lib/playbook/bottom.rb +8 -12
  209. data/lib/playbook/classnames.rb +65 -49
  210. data/lib/playbook/cursor.rb +7 -10
  211. data/lib/playbook/display.rb +16 -22
  212. data/lib/playbook/flex.rb +17 -28
  213. data/lib/playbook/flex_direction.rb +17 -28
  214. data/lib/playbook/flex_grow.rb +17 -28
  215. data/lib/playbook/flex_shrink.rb +17 -28
  216. data/lib/playbook/flex_wrap.rb +17 -28
  217. data/lib/playbook/height.rb +7 -10
  218. data/lib/playbook/hover.rb +31 -27
  219. data/lib/playbook/justify_content.rb +17 -28
  220. data/lib/playbook/justify_self.rb +17 -28
  221. data/lib/playbook/kit_base.rb +33 -16
  222. data/lib/playbook/left.rb +8 -12
  223. data/lib/playbook/line_height.rb +7 -10
  224. data/lib/playbook/max_height.rb +7 -10
  225. data/lib/playbook/min_height.rb +7 -10
  226. data/lib/playbook/number_spacing.rb +7 -10
  227. data/lib/playbook/order.rb +17 -28
  228. data/lib/playbook/overflow.rb +13 -12
  229. data/lib/playbook/position.rb +7 -12
  230. data/lib/playbook/props.rb +24 -5
  231. data/lib/playbook/right.rb +8 -12
  232. data/lib/playbook/shadow.rb +7 -10
  233. data/lib/playbook/spacing.rb +100 -128
  234. data/lib/playbook/text_align.rb +17 -28
  235. data/lib/playbook/top.rb +8 -12
  236. data/lib/playbook/truncate.rb +7 -10
  237. data/lib/playbook/version.rb +2 -2
  238. data/lib/playbook/vertical_align.rb +17 -28
  239. data/lib/playbook/z_index.rb +17 -26
  240. metadata +135 -9
  241. data/app/pb_kits/playbook/pb_button/docs/_button_full_width.md +0 -1
  242. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +0 -58
  243. data/dist/chunks/_typeahead-CpQ_F870.js +0 -1
  244. data/dist/chunks/lib-9kTaI6nm.js +0 -29
@@ -7,6 +7,7 @@ examples:
7
7
  - advanced_table_table_props: Table Props
8
8
  - advanced_table_sticky_header_rails: Sticky Header
9
9
  - advanced_table_table_props_sticky_header: Sticky Header for Responsive Table
10
+ - advanced_table_pinned_rows_rails: Pinned Rows
10
11
  - advanced_table_beta_sort: Enable Sorting
11
12
  - advanced_table_responsive: Responsive Tables
12
13
  - advanced_table_custom_cell_rails: Custom Components for Cells
@@ -74,6 +75,7 @@ examples:
74
75
  - advanced_table_column_visibility_with_state: Column Visibility Control With State
75
76
  - advanced_table_column_visibility_custom: Column Visibility Control with Custom Dropdown
76
77
  - advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
78
+ - advanced_table_grouped_headers_composition: Grouped headers, custom headers, sort, and pinned rows
77
79
  - advanced_table_scrollbar_none: Advanced Table Scrollbar None
78
80
  - advanced_table_row_styling: Row Styling
79
81
  - advanced_table_padding_control_per_row: Padding Control using Row Styling
@@ -42,6 +42,7 @@ export { default as AdvancedTableInfiniteScroll} from './_advanced_table_infinit
42
42
  export {default as AdvancedTableWithCustomHeader} from './_advanced_table_with_custom_header.jsx'
43
43
  export { default as AdvancedTableCustomSort } from './_advanced_table_custom_sort.jsx'
44
44
  export { default as AdvancedTableWithCustomHeaderMultiHeader } from './_advanced_table_with_custom_header_multi_header.jsx'
45
+ export { default as AdvancedTableGroupedHeadersComposition } from './_advanced_table_grouped_headers_composition.jsx'
45
46
  export { default as AdvancedTableSortPerColumn } from './_advanced_table_sort_per_column.jsx'
46
47
  export { default as AdvancedTableSortPerColumnForMultiColumn } from './_advanced_table_sort_per_column_for_multi_column.jsx'
47
48
  export { default as AdvancedTablePaddingControl } from './_advanced_table_padding_control.jsx'
@@ -196,6 +196,17 @@ export default class PbAdvancedTable extends PbEnhancedElement {
196
196
  if (table.dataset.pbAdvancedTableInitialized) return;
197
197
  table.dataset.pbAdvancedTableInitialized = "true";
198
198
 
199
+ // Measure header height so pinned rows don't overlap when header wraps (e.g. mobile)
200
+ if (mainTable) {
201
+ PbAdvancedTable.updateStickyHeaderRowHeights(mainTable);
202
+ const resizeObserver = new ResizeObserver(() => {
203
+ PbAdvancedTable.updateStickyHeaderRowHeights(mainTable);
204
+ PbAdvancedTable.updatePinnedRowsStickyTops(mainTable);
205
+ });
206
+ resizeObserver.observe(table);
207
+ mainTable._advancedTableHeaderResizeObserver = resizeObserver;
208
+ }
209
+
199
210
  // Delegate checkbox changes
200
211
  table.addEventListener("change", (event) => {
201
212
  const checkbox = event.target.closest('input[type="checkbox"]');
@@ -315,9 +326,64 @@ export default class PbAdvancedTable extends PbEnhancedElement {
315
326
  lastVisibleRow.classList.add("last-visible-row");
316
327
  lastVisibleRow.classList.add("last-row-cell");
317
328
  }
329
+
330
+ PbAdvancedTable.updateStickyHeaderRowHeights(parentElement);
331
+ PbAdvancedTable.updatePinnedRowsStickyTops(table);
318
332
  }
319
333
  }
320
334
 
335
+ /**
336
+ * Measure thead height and set --advanced-table-header-height so pinned rows and
337
+ * multi-row sticky headers use the correct offset. Re-run when header wraps (e.g. mobile).
338
+ */
339
+ static updateStickyHeaderRowHeights(advancedTableWrapper) {
340
+ if (!advancedTableWrapper) return;
341
+ const table = advancedTableWrapper.querySelector("table.pb_table");
342
+ const thead = table?.querySelector("thead");
343
+ if (!thead) return;
344
+
345
+ const rows = Array.from(thead.querySelectorAll("tr"));
346
+ let totalHeight = 0;
347
+ rows.forEach((tr, index) => {
348
+ const h = tr.offsetHeight;
349
+ if (index === 0) {
350
+ advancedTableWrapper.style.setProperty(
351
+ "--advanced-table-header-row-0-height",
352
+ `${h}px`
353
+ );
354
+ } else if (index === 1) {
355
+ advancedTableWrapper.style.setProperty(
356
+ "--advanced-table-header-row-1-height",
357
+ `${h}px`
358
+ );
359
+ }
360
+ totalHeight += h;
361
+ });
362
+ advancedTableWrapper.style.setProperty(
363
+ "--advanced-table-header-height",
364
+ `${totalHeight}px`
365
+ );
366
+ }
367
+
368
+ /**
369
+ * Recompute sticky top for visible pinned rows so collapsed rows don't leave a gap.
370
+ * Call after expand/collapse and on load.
371
+ */
372
+ static updatePinnedRowsStickyTops(advancedTableWrapper) {
373
+ const pinnedTbody = advancedTableWrapper?.querySelector("tbody.pinned-rows-tbody");
374
+ if (!pinnedTbody) return;
375
+
376
+ const pinnedRows = Array.from(pinnedTbody.querySelectorAll("tr.pinned-row"));
377
+ const visibleRows = pinnedRows.filter(
378
+ (tr) => tr.style.display !== "none" && tr.offsetParent !== null
379
+ );
380
+
381
+ const headerOffset = "var(--advanced-table-header-height, 44px)";
382
+ visibleRows.forEach((tr, index) => {
383
+ tr.style.top = `calc(${headerOffset} + 2.5em * ${index})`;
384
+ });
385
+ }
386
+
321
387
  hideCloseIcon() {
322
388
  const closeIcon = this.element.querySelector(UP_ARROW_SELECTOR);
323
389
  closeIcon.style.display = "none";
@@ -519,3 +585,19 @@ window.expandAllRows = (element) => {
519
585
  window.expandAllSubRows = (element, rowDepth) => {
520
586
  PbAdvancedTable.handleToggleAllSubRows(element, rowDepth);
521
587
  };
588
+
589
+ // Fix header height and pinned row sticky tops on load (header wrap + collapsed rows)
590
+ function updateAllAdvancedTableStickyHeights() {
591
+ document.querySelectorAll(".pb_advanced_table").forEach((wrapper) => {
592
+ PbAdvancedTable.updateStickyHeaderRowHeights(wrapper);
593
+ PbAdvancedTable.updatePinnedRowsStickyTops(wrapper);
594
+ });
595
+ }
596
+
597
+ if (typeof document !== "undefined") {
598
+ if (document.readyState === "loading") {
599
+ document.addEventListener("DOMContentLoaded", updateAllAdvancedTableStickyHeights);
600
+ } else {
601
+ updateAllAdvancedTableStickyHeights();
602
+ }
603
+ }
@@ -0,0 +1,278 @@
1
+ {
2
+ "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
+ "name": "AdvancedTable",
4
+ "description": "AdvancedTable component",
5
+ "platforms": [
6
+ "react",
7
+ "rails"
8
+ ],
9
+ "props": {
10
+ "actions": {
11
+ "type": "React.ReactNode[] | React.ReactNode",
12
+ "platforms": [
13
+ "react",
14
+ "rails"
15
+ ]
16
+ },
17
+ "cascadeCollapse": {
18
+ "type": "boolean",
19
+ "platforms": [
20
+ "react"
21
+ ]
22
+ },
23
+ "columnDefinitions": {
24
+ "type": "array",
25
+ "platforms": [
26
+ "react",
27
+ "rails"
28
+ ]
29
+ },
30
+ "columnGroupBorderColor": {
31
+ "type": "enum",
32
+ "platforms": [
33
+ "react",
34
+ "rails"
35
+ ],
36
+ "values": [
37
+ "text_lt_default",
38
+ "text_lt_light",
39
+ "text_lt_lighter",
40
+ "text_dk_default",
41
+ "text_dk_light",
42
+ "text_dk_lighter"
43
+ ],
44
+ "default": "none"
45
+ },
46
+ "columnVisibilityControl": {
47
+ "type": "GenericObject",
48
+ "platforms": [
49
+ "react"
50
+ ]
51
+ },
52
+ "customSort": {
53
+ "type": "boolean;",
54
+ "platforms": [
55
+ "react"
56
+ ]
57
+ },
58
+ "enableToggleExpansion": {
59
+ "type": "enum",
60
+ "platforms": [
61
+ "react",
62
+ "rails"
63
+ ],
64
+ "values": [
65
+ "all",
66
+ "header",
67
+ "none"
68
+ ],
69
+ "default": "header"
70
+ },
71
+ "enableSortingRemoval": {
72
+ "type": "boolean",
73
+ "platforms": [
74
+ "react"
75
+ ]
76
+ },
77
+ "expandedControl": {
78
+ "type": "GenericObject",
79
+ "platforms": [
80
+ "react"
81
+ ]
82
+ },
83
+ "expandByDepth": {
84
+ "type": "{ [key: string]: string | number }",
85
+ "platforms": [
86
+ "react"
87
+ ]
88
+ },
89
+ "onExpandByDepthClick": {
90
+ "type": "function",
91
+ "platforms": [
92
+ "react"
93
+ ]
94
+ },
95
+ "initialLoadingRowsCount": {
96
+ "type": "number",
97
+ "platforms": [
98
+ "react"
99
+ ]
100
+ },
101
+ "inlineRowLoading": {
102
+ "type": "boolean",
103
+ "platforms": [
104
+ "react",
105
+ "rails"
106
+ ],
107
+ "default": false
108
+ },
109
+ "loading": {
110
+ "type": "boolean | string",
111
+ "platforms": [
112
+ "react",
113
+ "rails"
114
+ ],
115
+ "default": false
116
+ },
117
+ "onRowToggleClick": {
118
+ "type": "function",
119
+ "platforms": [
120
+ "react"
121
+ ]
122
+ },
123
+ "onToggleExpansionClick": {
124
+ "type": "function",
125
+ "platforms": [
126
+ "react"
127
+ ]
128
+ },
129
+ "pagination": {
130
+ "type": "boolean",
131
+ "platforms": [
132
+ "react"
133
+ ]
134
+ },
135
+ "paginationProps": {
136
+ "type": "function",
137
+ "platforms": [
138
+ "react"
139
+ ]
140
+ },
141
+ "responsive": {
142
+ "type": "enum",
143
+ "platforms": [
144
+ "react",
145
+ "rails"
146
+ ],
147
+ "values": [
148
+ "scroll",
149
+ "none"
150
+ ],
151
+ "default": "scroll"
152
+ },
153
+ "rowStyling": {
154
+ "type": "array",
155
+ "platforms": [
156
+ "react",
157
+ "rails"
158
+ ]
159
+ },
160
+ "scrollBarNone": {
161
+ "type": "boolean",
162
+ "platforms": [
163
+ "react",
164
+ "rails"
165
+ ],
166
+ "default": false
167
+ },
168
+ "selectableRows": {
169
+ "type": "boolean",
170
+ "platforms": [
171
+ "react",
172
+ "rails"
173
+ ],
174
+ "default": false
175
+ },
176
+ "showActionsBar": {
177
+ "type": "boolean",
178
+ "platforms": [
179
+ "react",
180
+ "rails"
181
+ ],
182
+ "default": true
183
+ },
184
+ "persistToggleExpansionButton": {
185
+ "type": "boolean",
186
+ "platforms": [
187
+ "react",
188
+ "rails"
189
+ ],
190
+ "default": false
191
+ },
192
+ "sortControl": {
193
+ "type": "GenericObject",
194
+ "platforms": [
195
+ "react"
196
+ ]
197
+ },
198
+ "sortParentOnly": {
199
+ "type": "boolean",
200
+ "platforms": [
201
+ "react"
202
+ ]
203
+ },
204
+ "tableData": {
205
+ "type": "array",
206
+ "platforms": [
207
+ "react",
208
+ "rails"
209
+ ]
210
+ },
211
+ "tableOptions": {
212
+ "type": "GenericObject",
213
+ "platforms": [
214
+ "react"
215
+ ]
216
+ },
217
+ "tableProps": {
218
+ "type": "GenericObject",
219
+ "platforms": [
220
+ "react",
221
+ "rails"
222
+ ]
223
+ },
224
+ "toggleExpansionIcon": {
225
+ "type": "string | string[]",
226
+ "platforms": [
227
+ "react"
228
+ ]
229
+ },
230
+ "onRowSelectionChange": {
231
+ "type": "function",
232
+ "platforms": [
233
+ "react"
234
+ ]
235
+ },
236
+ "onCustomSortClick": {
237
+ "type": "function",
238
+ "platforms": [
239
+ "react"
240
+ ]
241
+ },
242
+ "virtualizedRows": {
243
+ "type": "boolean",
244
+ "platforms": [
245
+ "react"
246
+ ]
247
+ },
248
+ "allowFullScreen": {
249
+ "type": "boolean",
250
+ "platforms": [
251
+ "react"
252
+ ]
253
+ },
254
+ "fullScreenControl": {
255
+ "type": "function",
256
+ "platforms": [
257
+ "react"
258
+ ]
259
+ },
260
+ "pinnedRows": {
261
+ "platforms": [
262
+ "rails"
263
+ ],
264
+ "type": "string"
265
+ }
266
+ },
267
+ "globalProps": true,
268
+ "usage": {
269
+ "react": {
270
+ "import": "import { AdvancedTable } from 'playbook-ui'",
271
+ "example": "<AdvancedTable columnGroupBorderColor=\"text_lt_default\" enableToggleExpansion=\"all\"></AdvancedTable>"
272
+ },
273
+ "rails": {
274
+ "import": null,
275
+ "example": "<%= pb_rails(\"advanced_table\", props: { column_group_border_color: \"text_lt_default\", enable_toggle_expansion: \"all\" }) %>"
276
+ }
277
+ }
278
+ }
@@ -1,3 +1,9 @@
1
+ // Acts as outer “card frame” on the advanced-table wrapper (table-card from Table) is included from `_advanced_table.scss` only when `:not(.advanced-table-no-table-container)`/container: false is not present.
2
+ @mixin advanced-table-sticky-wrapper-frame($border-color) {
3
+ border-radius: 4px;
4
+ box-shadow: 1px 0 0 0px $border-color, -1px 0 0 0px $border-color;
5
+ }
6
+
1
7
  @mixin advanced-table-sticky-mixin(
2
8
  $border-color,
3
9
  $bg-main,
@@ -5,8 +11,6 @@
5
11
  $highlight: #E5EEFA,
6
12
  $highlight-dark: #202850,
7
13
  ) {
8
- border-radius: 4px;
9
- box-shadow: 1px 0 0 0px $border-color, -1px 0 0 0px $border-color;
10
14
  display: block;
11
15
  [class^="pb_table"].table-sm.table-card thead tr th:first-child,
12
16
  [class^="pb_table"].table-sm:not(.no-hover).table-card
@@ -1,5 +1,22 @@
1
- <%= pb_content_tag(:tbody) do %>
2
- <% object.table_data.each do |row| %>
3
- <%= render_row_and_children(row, object.column_definitions, 0, false) %>
4
- <% end %>
1
+ <% table_data = object.table_data || [] %>
2
+ <% if object.has_pinned_rows? %>
3
+ <%= pb_content_tag(:tbody, class: "pinned-rows-tbody") do %>
4
+ <% next_index = 0 %>
5
+ <% object.pinned_root_rows.each do |root_info| %>
6
+ <% row_output, next_index = object.render_row_and_children(root_info[:row], object.column_definitions, root_info[:depth], root_info[:depth] > 0, root_info[:ancestor_ids] || [], root_info[:ancestor_ids]&.first, immediate_parent_row_id: object.row_id_for(root_info[:parent_row]), is_pinned_row: true, pinned_index: next_index, initial_table_data_attributes: root_info[:depth].to_i > 0 ? object.pinned_root_initial_data_attributes(root_info) : nil) %>
7
+ <%= row_output %>
8
+ <% end %>
9
+ <% end %>
10
+ <%= pb_content_tag(:tbody) do %>
11
+ <% table_data.each do |row| %>
12
+ <% result = object.render_row_and_children(row, object.column_definitions, 0, false, skip_pinned_ids: object.pinned_ids_set) %>
13
+ <%= result.is_a?(Array) ? result[0] : result %>
14
+ <% end %>
15
+ <% end %>
16
+ <% else %>
17
+ <%= pb_content_tag(:tbody) do %>
18
+ <% table_data.each do |row| %>
19
+ <%= object.render_row_and_children(row, object.column_definitions, 0, false) %>
20
+ <% end %>
21
+ <% end %>
5
22
  <% end %>
@@ -27,6 +27,8 @@ module Playbook
27
27
  default: []
28
28
  prop :inline_row_loading, type: Playbook::Props::Boolean,
29
29
  default: false
30
+ prop :pinned_rows, type: Playbook::Props::HashProp,
31
+ default: {}
30
32
 
31
33
  def flatten_columns(columns)
32
34
  columns.flat_map do |col|
@@ -42,14 +44,21 @@ module Playbook
42
44
  end.compact
43
45
  end
44
46
 
45
- def render_row_and_children(row, column_definitions, current_depth, first_parent_child, ancestor_ids = [], top_parent_id = nil, additional_classes: "", table_data_attributes: {}, immediate_parent_row_id: nil)
47
+ def render_row_and_children(row, column_definitions, current_depth, first_parent_child, ancestor_ids = [], top_parent_id = nil, additional_classes: "", table_data_attributes: {}, immediate_parent_row_id: nil, is_pinned_row: false, pinned_index: nil, skip_pinned_ids: nil, initial_table_data_attributes: nil)
48
+ if skip_pinned_ids && row_id_for(row) && skip_pinned_ids.include?(row_id_for(row).to_s)
49
+ return is_pinned_row ? [ActiveSupport::SafeBuffer.new, pinned_index] : ActiveSupport::SafeBuffer.new
50
+ end
51
+
46
52
  top_parent_id ||= row.object_id
47
53
  new_ancestor_ids = ancestor_ids + [row.object_id]
48
- leaf_columns = flatten_columns(column_definitions)
54
+ leaf_columns = flatten_columns(column_definitions || [])
49
55
 
50
56
  output = ActiveSupport::SafeBuffer.new
51
- is_first_child_of_subrow = current_depth.positive? && first_parent_child && subrow_headers[current_depth - 1].present?
52
- last_row = subrow_headers.length == current_depth
57
+ subrow_headers_arr = subrow_headers || []
58
+ is_first_child_of_subrow = current_depth.positive? && first_parent_child && subrow_headers_arr[current_depth - 1].present?
59
+ last_row = subrow_headers_arr.length == current_depth
60
+
61
+ next_pinned_index = pinned_index
53
62
 
54
63
  subrow_ancestor_ids = ancestor_ids + ["#{row.object_id}sr"]
55
64
  subrow_data_attributes = {
@@ -58,7 +67,16 @@ module Playbook
58
67
  row_parent: "#{table_id}_#{ancestor_ids.last}",
59
68
  }
60
69
  # Subrow header if applicable
61
- output << pb_rails("advanced_table/table_subrow_header", props: { row: row, column_definitions: leaf_columns, depth: current_depth, subrow_header: subrow_headers[current_depth - 1], collapsible_trail: collapsible_trail, classname: "toggle-content", responsive: responsive, subrow_data_attributes: subrow_data_attributes, last_row: last_row, immediate_parent_row_id: immediate_parent_row_id }) if is_first_child_of_subrow && enable_toggle_expansion == "all"
70
+ if is_first_child_of_subrow && enable_toggle_expansion == "all"
71
+ subrow_props = { row: row, column_definitions: leaf_columns, depth: current_depth, subrow_header: subrow_headers_arr[current_depth - 1], collapsible_trail: collapsible_trail, classname: "toggle-content", responsive: responsive, subrow_data_attributes: subrow_data_attributes, last_row: last_row, immediate_parent_row_id: immediate_parent_row_id }
72
+ if is_pinned_row && next_pinned_index
73
+ subrow_props[:is_pinned_row] = true
74
+ subrow_props[:pinned_index] = next_pinned_index
75
+ subrow_props[:html_options] = { style: build_pinned_row_style(next_pinned_index, background: "var(--pb_table_sticky_bg, #f5f5f5)") }
76
+ next_pinned_index += 1
77
+ end
78
+ output << pb_rails("advanced_table/table_subrow_header", props: subrow_props)
79
+ end
62
80
 
63
81
  current_data_attributes = if current_depth.zero?
64
82
  {
@@ -67,11 +85,19 @@ module Playbook
67
85
  row_parent: nil,
68
86
  }
69
87
  else
70
- table_data_attributes
88
+ initial_table_data_attributes || table_data_attributes
71
89
  end
72
90
 
73
91
  # Additional class and data attributes needed for toggle logic
74
- output << pb_rails("advanced_table/table_row", props: { table_id: table_id, row: row, column_definitions: leaf_columns, depth: current_depth, collapsible_trail: collapsible_trail, classname: additional_classes, table_data_attributes: current_data_attributes, responsive: responsive, loading: loading, selectable_rows: selectable_rows, row_id: row[:id], enable_toggle_expansion: enable_toggle_expansion, row_styling: row_styling, last_row: last_row, immediate_parent_row_id: immediate_parent_row_id, inline_row_loading: inline_row_loading })
92
+ row_props = { table_id: table_id, row: row, column_definitions: leaf_columns, depth: current_depth, collapsible_trail: collapsible_trail, classname: additional_classes, table_data_attributes: current_data_attributes, responsive: responsive, loading: loading, selectable_rows: selectable_rows, row_id: row[:id], enable_toggle_expansion: enable_toggle_expansion, row_styling: row_styling, last_row: last_row, immediate_parent_row_id: immediate_parent_row_id, inline_row_loading: inline_row_loading }
93
+ if is_pinned_row && next_pinned_index
94
+ row_props[:is_pinned_row] = true
95
+ row_props[:pinned_index] = next_pinned_index
96
+ row_bg = (row_styling || []).find { |s| s[:row_id].to_s == row_id_for(row).to_s }&.[](:background_color) || "white"
97
+ row_props[:html_options] = { style: build_pinned_row_style(next_pinned_index, background: row_bg) }
98
+ next_pinned_index += 1
99
+ end
100
+ output << pb_rails("advanced_table/table_row", props: row_props)
75
101
 
76
102
  # Render inline loading row when inline_row_loading is enabled and row has empty children
77
103
  if inline_row_loading
@@ -103,11 +129,21 @@ module Playbook
103
129
  advanced_table_content: data_content,
104
130
  }
105
131
 
106
- output << render_row_and_children(child_row, column_definitions, current_depth + 1, is_first_child, new_ancestor_ids, top_parent_id, additional_classes: "toggle-content", table_data_attributes: child_data_attributes, immediate_parent_row_id: row[:id])
132
+ child_opts = { additional_classes: "toggle-content", table_data_attributes: child_data_attributes, immediate_parent_row_id: row[:id] }
133
+ child_opts[:is_pinned_row] = is_pinned_row
134
+ child_opts[:pinned_index] = next_pinned_index if is_pinned_row
135
+ child_opts[:skip_pinned_ids] = skip_pinned_ids if skip_pinned_ids
136
+
137
+ child_output, next_pinned_index = render_row_and_children(child_row, column_definitions, current_depth + 1, is_first_child, new_ancestor_ids, top_parent_id, **child_opts)
138
+ output << child_output
107
139
  end
108
140
  end
109
141
 
110
- output
142
+ if is_pinned_row
143
+ [output, next_pinned_index]
144
+ else
145
+ output
146
+ end
111
147
  end
112
148
 
113
149
  def classname
@@ -142,6 +178,76 @@ module Playbook
142
178
  end
143
179
  end
144
180
 
181
+ def row_id_for(row)
182
+ return nil if row.nil?
183
+
184
+ row[:id] || row["id"]
185
+ end
186
+
187
+ def pinned_top_ids
188
+ return [] if pinned_rows.nil? || !pinned_rows.respond_to?(:[])
189
+
190
+ top = pinned_rows["top"] || pinned_rows[:top]
191
+ Array(top).map(&:to_s)
192
+ end
193
+
194
+ def pinned_ids_set
195
+ return Set.new if pinned_top_ids.blank?
196
+
197
+ set = Set.new
198
+ pinned_root_rows.each do |root|
199
+ collect_row_and_descendant_ids(root[:row], set)
200
+ end
201
+ set
202
+ end
203
+
204
+ def collect_row_and_descendant_ids(row, set)
205
+ id = row_id_for(row)
206
+ set.add(id.to_s) if id
207
+ row_children_for(row)&.each { |child| collect_row_and_descendant_ids(child, set) }
208
+ end
209
+
210
+ def find_row_by_id(data, id, depth: 0, ancestor_ids: [], parent_row: nil)
211
+ id_str = id.to_s
212
+ Array(data).each do |row|
213
+ return { row: row, depth: depth, ancestor_ids: ancestor_ids, parent_row: parent_row } if row_id_for(row).to_s == id_str
214
+
215
+ found = find_row_by_id(row_children_for(row), id_str, depth: depth + 1, ancestor_ids: ancestor_ids + [row.object_id], parent_row: row)
216
+ return found if found
217
+ end
218
+ nil
219
+ end
220
+
221
+ def pinned_root_rows
222
+ return [] if pinned_top_ids.blank?
223
+
224
+ pinned_top_ids.filter_map { |id| find_row_by_id(table_data, id) }
225
+ end
226
+
227
+ def has_pinned_rows?
228
+ pinned_root_rows.any?
229
+ end
230
+
231
+ # Build inline style for sticky pinned row (matches React). Pass via html_options so the tr gets the attribute.
232
+ def build_pinned_row_style(pinned_index, background: "white")
233
+ header_offset = "var(--advanced-table-header-height, 44px)"
234
+ row_offset = "calc(2.5em * #{pinned_index})"
235
+ "position: sticky; top: calc(#{header_offset} + #{row_offset}); z-index: 3; background: #{background};"
236
+ end
237
+
238
+ def pinned_root_initial_data_attributes(root_info)
239
+ return {} if root_info[:depth].to_i.zero?
240
+
241
+ anc = root_info[:ancestor_ids] || []
242
+ content = (anc + [root_info[:row].object_id]).join("-")
243
+ {
244
+ top_parent: "#{table_id}_#{anc.first}",
245
+ row_depth: root_info[:depth],
246
+ row_parent: "#{table_id}_#{anc.last}",
247
+ advanced_table_content: content,
248
+ }
249
+ end
250
+
145
251
  def cell_accessors_length(col_defs)
146
252
  first_col = col_defs.first
147
253
  return 0 unless first_col
@@ -7,7 +7,7 @@
7
7
  <% header_row.each_with_index do |cell, cell_index| %>
8
8
  <% header_component = object.header_component_info(cell, cell_index, row_index) %>
9
9
  <%= pb_rails(header_component[:name], props: header_component[:props]) do %>
10
- <%= pb_rails("flex", props: { align: "center", justify: cell_index.zero? ? "start" : row_index === header_rows.size - 1 ? "end" : "center", text_align: (cell[:header_alignment] || "end") }) do %>
10
+ <%= pb_rails("flex", props: { align: "center", justify: object.header_flex_justify(cell, cell_index, row_index), text_align: object.header_flex_text_align(cell) }) do %>
11
11
  <% if cell_index.zero? && row_index === header_rows.size - 1 %>
12
12
  <% if object.selectable_rows && object.enable_toggle_expansion != "none" %>
13
13
  <%= pb_rails("flex/flex_item", props: { padding_right: "xs" }) do %>
@@ -192,6 +192,19 @@ module Playbook
192
192
  { name: component_name, props: component_props }
193
193
  end
194
194
 
195
+ # Flex justify for header cells: column_styling header_alignment when present (otherwisedefault by column/row index)
196
+ def header_flex_justify(cell, cell_index, row_index)
197
+ ha = cell[:header_alignment]
198
+ return header_alignment_to_justify(ha) if ha.present?
199
+
200
+ default_header_flex_justify(cell_index, row_index)
201
+ end
202
+
203
+ # Flex text_align from column_styling header_alignment (default is end)
204
+ def header_flex_text_align(cell)
205
+ (cell[:header_alignment].presence || "end").to_s
206
+ end
207
+
195
208
  private
196
209
 
197
210
  # Find the original column definition for a cell
@@ -351,6 +364,26 @@ module Playbook
351
364
  row[:children] || row["children"]
352
365
  end
353
366
  end
367
+
368
+ # 2 header alignment helper methods
369
+ def header_alignment_to_justify(header_alignment)
370
+ case header_alignment.to_s
371
+ when "left" then "start"
372
+ when "center" then "center"
373
+ when "right" then "end"
374
+ else "end"
375
+ end
376
+ end
377
+
378
+ def default_header_flex_justify(cell_index, row_index)
379
+ if cell_index.zero?
380
+ "start"
381
+ elsif row_index == header_rows.size - 1
382
+ "end"
383
+ else
384
+ "center"
385
+ end
386
+ end
354
387
  end
355
388
  end
356
389
  end