playbook_ui 14.10.0.pre.alpha.PBNTR775formmatingmaskdefaultvalue5137 → 14.10.0.pre.alpha.play1465attempt25272

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +32 -19
  3. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +3 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +35 -3
  5. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +32 -18
  6. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +1 -1
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +8 -1
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_sort.html.erb +1 -3
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers.html.erb +43 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers.jsx +60 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers.md +1 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers_multiple.html.erb +58 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers_multiple.jsx +74 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers_multiple.md +1 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +6 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/index.js +143 -95
  18. data/app/pb_kits/playbook/pb_advanced_table/table_body.html.erb +1 -1
  19. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +50 -8
  20. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +17 -14
  21. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +78 -0
  22. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +4 -3
  23. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +7 -2
  24. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +2 -6
  25. data/app/pb_kits/playbook/pb_card/_card.scss +21 -3
  26. data/app/pb_kits/playbook/pb_card/docs/_card_header.html.erb +18 -0
  27. data/app/pb_kits/playbook/pb_card/docs/_card_header.jsx +40 -0
  28. data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.tsx +3 -0
  29. data/app/pb_kits/playbook/pb_circle_icon_button/circle_icon_button.html.erb +1 -1
  30. data/app/pb_kits/playbook/pb_circle_icon_button/circle_icon_button.rb +1 -0
  31. data/app/pb_kits/playbook/pb_circle_icon_button/docs/_circle_icon_button_link.html.erb +8 -0
  32. data/app/pb_kits/playbook/pb_circle_icon_button/docs/_circle_icon_button_link.jsx +9 -0
  33. data/app/pb_kits/playbook/pb_circle_icon_button/docs/_circle_icon_button_link.md +1 -0
  34. data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.scss +1 -13
  35. data/app/pb_kits/playbook/pb_section_separator/_section_separator.scss +64 -1
  36. data/app/pb_kits/playbook/pb_section_separator/_section_separator.tsx +3 -1
  37. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.html.erb +10 -0
  38. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.jsx +42 -0
  39. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.md +3 -0
  40. data/app/pb_kits/playbook/pb_section_separator/docs/example.yml +2 -0
  41. data/app/pb_kits/playbook/pb_section_separator/docs/index.js +1 -0
  42. data/app/pb_kits/playbook/pb_section_separator/section_separator.rb +4 -1
  43. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.scss +2 -2
  44. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_description.md +3 -0
  45. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.html.erb +9 -0
  46. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius_rails.md +1 -0
  47. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.html.erb +7 -0
  48. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.html.erb +1 -1
  49. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_filter.html.erb +119 -0
  50. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_filter.jsx +10 -2
  51. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.html.erb +15 -0
  52. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.jsx +6 -2
  53. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width_rails.md +3 -0
  54. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.html.erb +3 -0
  55. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_user.html.erb +63 -0
  56. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_user.jsx +11 -3
  57. data/app/pb_kits/playbook/pb_skeleton_loading/docs/example.yml +7 -2
  58. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.html.erb +8 -12
  59. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.rb +48 -1
  60. data/app/pb_kits/playbook/pb_table/_table.tsx +67 -18
  61. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_columns.jsx +88 -0
  62. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_columns_react.md +3 -0
  63. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns.jsx +1 -1
  64. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns_rails.md +2 -0
  65. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns_react.md +4 -1
  66. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_right_columns.jsx +87 -0
  67. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_right_columns_react.md +5 -0
  68. data/app/pb_kits/playbook/pb_table/index.ts +17 -17
  69. data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +38 -2
  70. data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +17 -3
  71. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -2
  72. data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +12 -4
  73. data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +5 -1
  74. data/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx +8 -3
  75. data/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx +6 -1
  76. data/app/pb_kits/playbook/pb_typeahead/components/Option.tsx +21 -6
  77. data/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx +13 -6
  78. data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +7 -3
  79. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx +2 -0
  80. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx +4 -2
  81. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async_custom_options.jsx +5 -5
  82. data/dist/chunks/{_typeahead-CoIYBETL.js → _typeahead-BhT5aXCY.js} +3 -3
  83. data/dist/chunks/_weekday_stacked-DJOTNDSY.js +45 -0
  84. data/dist/chunks/{lib-sMFo2JZy.js → lib-B7sgJtGS.js} +1 -1
  85. data/dist/chunks/{pb_form_validation-CgvjWbOK.js → pb_form_validation-C5Cc0-1v.js} +1 -1
  86. data/dist/chunks/vendor.js +1 -1
  87. data/dist/menu.yml +1 -1
  88. data/dist/playbook-doc.js +1 -1
  89. data/dist/playbook-rails-react-bindings.js +1 -1
  90. data/dist/playbook-rails.js +1 -1
  91. data/dist/playbook.css +1 -1
  92. data/lib/playbook/version.rb +1 -1
  93. metadata +31 -8
  94. data/dist/chunks/_weekday_stacked-Qj3GFYzA.js +0 -45
  95. /data/app/pb_kits/playbook/pb_skeleton_loading/docs/{_skeleton_loading_border_radius.md → _skeleton_loading_border_radius_react.md} +0 -0
  96. /data/app/pb_kits/playbook/pb_skeleton_loading/docs/{_skeleton_loading_height_width.md → _skeleton_loading_height_width_react.md} +0 -0
@@ -1,138 +1,186 @@
1
- import PbEnhancedElement from '../pb_enhanced_element'
1
+ import PbEnhancedElement from "../pb_enhanced_element";
2
2
 
3
- const ADVANCED_TABLE_SELECTOR = '[data-advanced-table]'
4
- const CONTENT_SELECTOR = '[data-advanced-table-content="id"]'
5
- const DOWN_ARROW_SELECTOR = '#advanced-table_open_icon'
6
- const UP_ARROW_SELECTOR = '#advanced-table_close_icon'
3
+ const ADVANCED_TABLE_SELECTOR = "[data-advanced-table]";
4
+ const DOWN_ARROW_SELECTOR = "#advanced-table_open_icon";
5
+ const UP_ARROW_SELECTOR = "#advanced-table_close_icon";
7
6
 
8
7
  export default class PbAdvancedTable extends PbEnhancedElement {
9
8
  static get selector() {
10
- return ADVANCED_TABLE_SELECTOR
9
+ return ADVANCED_TABLE_SELECTOR;
11
10
  }
12
11
 
13
12
  get target() {
14
- return document.querySelector(CONTENT_SELECTOR.replace("id", this.element.id))
13
+ const table = this.element.closest("table");
14
+ return table.querySelectorAll(`[data-row-parent="${this.element.id}"]`);
15
15
  }
16
-
17
- static expandedRows = new Set()
18
- static isCollapsing = false
16
+
17
+ static expandedRows = new Set();
18
+ static isCollapsing = false;
19
19
 
20
20
  connect() {
21
- this.element.addEventListener('click', () => {
21
+ this.element.addEventListener("click", () => {
22
22
  if (!PbAdvancedTable.isCollapsing) {
23
- const isExpanded = this.element.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
23
+ const isExpanded =
24
+ this.element.querySelector(UP_ARROW_SELECTOR).style.display ===
25
+ "inline-block";
24
26
  if (!isExpanded) {
25
- PbAdvancedTable.expandedRows.add(this.element.id)
27
+ PbAdvancedTable.expandedRows.add(this.element.id);
26
28
  } else {
27
- PbAdvancedTable.expandedRows.delete(this.element.id)
29
+ PbAdvancedTable.expandedRows.delete(this.element.id);
28
30
  }
31
+ this.toggleElement(this.target);
29
32
  }
30
- this.toggleElement(this.target)
31
- })
33
+ });
34
+
35
+ const nestedButtons = this.element
36
+ .closest("table")
37
+ .querySelectorAll("[data-advanced-table]");
38
+ nestedButtons.forEach((button) => {
39
+ button.addEventListener("click", () => {
40
+ const isExpanded =
41
+ button.querySelector(UP_ARROW_SELECTOR).style.display ===
42
+ "inline-block";
43
+ if (isExpanded) {
44
+ PbAdvancedTable.expandedRows.add(button.id);
45
+ } else {
46
+ PbAdvancedTable.expandedRows.delete(button.id);
47
+ }
48
+ });
49
+ });
32
50
  }
33
51
 
34
- showElement(elem) {
35
- const getHeight = () => {
36
- elem.style.display = 'block'
37
- const height = elem.scrollHeight + 'px'
38
- elem.style.display = ''
39
- return height
40
- }
52
+ showElement(elements) {
53
+ elements.forEach((elem) => {
54
+ elem.style.display = "table-row";
55
+ elem.classList.add("is-visible");
56
+ const childRowsAll = this.element
57
+ .closest("table")
58
+ .querySelectorAll(
59
+ `[data-advanced-table-content^="${elem.dataset.advancedTableContent}-"]`
60
+ );
61
+
62
+ childRowsAll.forEach((childRow) => {
63
+ const dataContent = childRow.dataset.advancedTableContent;
64
+
65
+ if (!dataContent) {
66
+ return;
67
+ }
41
68
 
42
- const height = getHeight()
43
- elem.classList.add('is-visible')
44
- elem.style.height = height
45
- elem.style.overflow = "hidden"
69
+ // Split the dataContent to get all ancestor IDs, check against simpleExpandedRows
70
+ const ancestorIds = dataContent.split("-").slice(0, -1);
46
71
 
47
- window.setTimeout(() => {
48
- elem.style.height = ''
49
- elem.style.overflow = "visible"
50
- }, 250)
72
+ const prefixedAncestorIds = ancestorIds.map(
73
+ (id) => `${childRow.id}_${id}`
74
+ );
75
+ const allAncestorsExpanded = prefixedAncestorIds.every((id) =>
76
+ PbAdvancedTable.expandedRows.has(id)
77
+ );
78
+
79
+ if (allAncestorsExpanded) {
80
+ childRow.style.display = "table-row";
81
+ childRow.classList.add("is-visible");
82
+ } else {
83
+ childRow.style.display = "none";
84
+ childRow.classList.remove("is-visible");
85
+ }
86
+ });
87
+ });
51
88
  }
52
89
 
53
- hideElement(elem) {
54
- elem.style.height = elem.scrollHeight + 'px'
90
+ hideElement(elements) {
91
+ elements.forEach((elem) => {
92
+ elem.style.display = "none";
93
+ elem.classList.remove("is-visible");
55
94
 
56
- window.setTimeout(() => {
57
- elem.style.height = '0'
58
- elem.style.paddingTop = '0'
59
- elem.style.paddingBottom = '0'
60
- elem.style.overflow = "hidden"
61
- }, 1)
95
+ // Remove the row ID from expandedRows when this row is hidden
96
+ if (PbAdvancedTable.expandedRows.has(elem.id)) {
97
+ PbAdvancedTable.expandedRows.delete(elem.id);
98
+ }
62
99
 
63
- window.setTimeout(() => {
64
- elem.classList.remove('is-visible')
65
- elem.style.overflow = ""
66
- }, 200)
100
+ const childrenArray = elem.dataset.advancedTableContent.split("-");
101
+ const currentDepth = parseInt(elem.dataset.rowDepth);
102
+ if (childrenArray.length > currentDepth) {
103
+ // Find the child rows corresponding to this parent row
104
+ const childRows = this.element
105
+ .closest("table")
106
+ .querySelectorAll(
107
+ `[data-advanced-table-content^="${elem.dataset.advancedTableContent}-"]`
108
+ );
109
+
110
+ childRows.forEach((childRow) => {
111
+ childRow.style.display = "none";
112
+ childRow.classList.remove("is-visible");
113
+ });
114
+ }
115
+ });
67
116
  }
68
117
 
69
- toggleElement(elem) {
70
- if (elem.classList.contains('is-visible')) {
71
- this.hideElement(elem)
72
- this.displayDownArrow()
73
- return
74
- }
118
+ toggleElement(elements) {
119
+ if (!elements.length) return;
75
120
 
76
- this.showElement(elem)
77
- this.displayUpArrow()
121
+ const isVisible = elements[0].classList.contains("is-visible");
122
+ if (isVisible) {
123
+ this.hideElement(elements);
124
+ this.displayDownArrow();
125
+ } else {
126
+ this.showElement(elements);
127
+ this.displayUpArrow();
128
+ }
78
129
  }
79
130
 
80
131
  displayDownArrow() {
81
- this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'inline-block'
82
- this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'none'
132
+ this.element.querySelector(DOWN_ARROW_SELECTOR).style.display =
133
+ "inline-block";
134
+ this.element.querySelector(UP_ARROW_SELECTOR).style.display = "none";
83
135
  }
84
136
 
85
137
  displayUpArrow() {
86
- this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'inline-block'
87
- this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'none'
138
+ this.element.querySelector(UP_ARROW_SELECTOR).style.display =
139
+ "inline-block";
140
+ this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = "none";
88
141
  }
89
142
 
90
143
  static handleToggleAllHeaders(element) {
91
- const table = element.closest('.pb_table')
92
- const firstLevelButtons = table.querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]')
93
-
94
- const expandedRows = Array.from(firstLevelButtons).filter(button =>
95
- button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
96
- )
97
-
98
- if (expandedRows.length === firstLevelButtons.length) {
99
- expandedRows.forEach(button => {
100
- button.click()
101
- })
102
- this.expandedRows.clear()
144
+ const table = element.closest(".pb_table");
145
+ const firstLevelButtons = table.querySelectorAll(
146
+ ".pb_advanced_table_body > .pb_table_tr [data-advanced-table]"
147
+ );
148
+
149
+ const allExpanded = Array.from(firstLevelButtons).every(
150
+ (button) =>
151
+ button.querySelector(UP_ARROW_SELECTOR).style.display === "inline-block"
152
+ );
153
+
154
+ if (allExpanded) {
155
+ firstLevelButtons.forEach((button) => {
156
+ button.click();
157
+ PbAdvancedTable.expandedRows.delete(button.id);
158
+ });
103
159
  } else {
104
- firstLevelButtons.forEach(button => {
105
- if (!this.expandedRows.has(button.id)) {
106
- button.click()
160
+ firstLevelButtons.forEach((button) => {
161
+ if (!PbAdvancedTable.expandedRows.has(button.id)) {
162
+ button.click();
163
+ PbAdvancedTable.expandedRows.add(button.id);
107
164
  }
108
- })
109
- }
110
- }
111
- static handleToggleAllSubRows(element, rowDepth) {
112
- const parentElement = element.closest(".toggle-content")
113
- const subrowButtons = parentElement.querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]')
114
-
115
- const expandedSubRows = Array.from(subrowButtons).filter(button =>
116
- button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
117
- )
118
-
119
- if (expandedSubRows.length === subrowButtons.length) {
120
- expandedSubRows.forEach(button => {
121
- button.click()
122
- })
123
- } else {
124
- subrowButtons.forEach(button => {
125
- if (!this.expandedRows.has(button.id)) {
126
- button.click()
165
+ });
166
+
167
+ PbAdvancedTable.expandedRows.forEach((rowId) => {
168
+ const nestedButton = table.querySelector(
169
+ `[data-advanced-table][id="${rowId}"]`
170
+ );
171
+ if (nestedButton && !firstLevelButtons.contains(nestedButton)) {
172
+ nestedButton.click();
127
173
  }
128
- })
174
+ });
129
175
  }
130
176
  }
177
+
178
+ // static handleToggleAllSubRows(element, rowDepth) {}
131
179
  }
132
180
 
133
181
  window.expandAllRows = (element) => {
134
- PbAdvancedTable.handleToggleAllHeaders(element)
135
- }
136
- window.expandAllSubRows = (element, rowDepth) => {
137
- PbAdvancedTable.handleToggleAllSubRows(element, rowDepth)
138
- }
182
+ PbAdvancedTable.handleToggleAllHeaders(element);
183
+ };
184
+ // window.expandAllSubRows = (element, rowDepth) => {
185
+ // PbAdvancedTable.handleToggleAllSubRows(element, rowDepth);
186
+ // };
@@ -1,4 +1,4 @@
1
- <%= pb_content_tag do %>
1
+ <%= pb_content_tag(:tbody) do %>
2
2
  <% object.table_data.each do |row| %>
3
3
  <%= render_row_and_children(row, object.column_definitions, 0, false) %>
4
4
  <% end %>
@@ -17,27 +17,69 @@ module Playbook
17
17
  prop :collapsible_trail, type: Playbook::Props::Boolean,
18
18
  default: true
19
19
 
20
- def render_row_and_children(row, column_definitions, current_depth, first_parent_child)
20
+ def flatten_columns(columns)
21
+ columns.flat_map do |col|
22
+ if col[:columns]
23
+ flatten_columns(col[:columns])
24
+ elsif col[:accessor].present?
25
+ if has_grouped_headers?
26
+ col.merge(is_last_in_group: last_in_group?(columns, col))
27
+ else
28
+ col
29
+ end
30
+ end
31
+ end.compact
32
+ end
33
+
34
+ def render_row_and_children(row, column_definitions, current_depth, first_parent_child, ancestor_ids = [], top_parent_id = nil)
35
+ top_parent_id ||= row.object_id
36
+ new_ancestor_ids = ancestor_ids + [row.object_id]
37
+ leaf_columns = flatten_columns(column_definitions)
38
+
21
39
  output = ActiveSupport::SafeBuffer.new
22
40
  is_first_child_of_subrow = current_depth.positive? && first_parent_child && subrow_headers[current_depth - 1].present?
23
41
 
24
42
  output << pb_rails("advanced_table/table_subrow_header", props: { row: row, column_definitions: column_definitions, depth: current_depth, subrow_header: subrow_headers[current_depth - 1], collapsible_trail: collapsible_trail }) if is_first_child_of_subrow && enable_toggle_expansion == "all"
25
43
 
26
- output << pb_rails("advanced_table/table_row", props: { id: id, row: row, column_definitions: column_definitions, depth: current_depth, collapsible_trail: collapsible_trail })
44
+ # Pass only leaf_columns to table_row to account for multiple nested columns
45
+ output << pb_rails("advanced_table/table_row", props: {
46
+ id: id,
47
+ row: row,
48
+ column_definitions: leaf_columns,
49
+ depth: current_depth,
50
+ collapsible_trail: collapsible_trail,
51
+ })
27
52
 
28
53
  if row[:children].present?
29
- output << content_tag(:div, class: "toggle-content", data: { advanced_table_content: row.object_id.to_s + id }) do
30
- row[:children].map do |child_row|
31
- render_row_and_children(child_row, column_definitions, current_depth + 1, row.children.first == child_row)
32
- end.join.html_safe
33
- end
54
+ output << row[:children].map do |child_row|
55
+ is_first_child = row[:children].first == child_row
56
+
57
+ child_output = render_row_and_children(child_row, column_definitions, current_depth + 1, is_first_child, new_ancestor_ids, top_parent_id)
58
+
59
+ immediate_parent_id = row.object_id
60
+ top_parent = top_parent_id
61
+ # Combine ancestor_ids to build the content id
62
+ data_content = new_ancestor_ids.join("-") + "-#{child_row.object_id}"
63
+
64
+ child_output.to_str.sub("<tr", %(<tr class="toggle-content" data-top-parent="#{id}_#{top_parent}" data-row-depth="#{current_depth}" data-row-parent="#{id}_#{immediate_parent_id}" data-advanced-table-content="#{data_content}"))
65
+ end.join.html_safe
34
66
  end
35
67
 
36
68
  output
37
69
  end
38
70
 
39
71
  def classname
40
- generate_classname("pb_advanced_table_body", "pb_table_tbody", separator: " ")
72
+ generate_classname("pb_advanced_table_body", separator: " ")
73
+ end
74
+
75
+ private
76
+
77
+ def has_grouped_headers?
78
+ column_definitions.any? { |col| col.key?(:columns) }
79
+ end
80
+
81
+ def last_in_group?(columns, current_col)
82
+ columns.last[:accessor] == current_col[:accessor]
41
83
  end
42
84
  end
43
85
  end
@@ -1,18 +1,21 @@
1
- <%= pb_content_tag do %>
2
- <%= pb_rails("table/table_row", props: { tag: "div" }) do %>
3
- <% object.column_definitions.each_with_index do |item, index| %>
4
- <%= pb_rails("table/table_header", props: { tag: "div", id: item[:accessor], classname: object.th_classname, sort_menu: item[:sort_menu] }) do %>
5
- <%= pb_rails("flex", props:{ align: "center", justify: index.zero? ? "start" : "end", text_align: "end" }) do %>
6
- <% if index.zero? && (object.enable_toggle_expansion == "header" || object.enable_toggle_expansion == "all") %>
7
- <button
8
- class="gray-icon toggle-all-icon"
9
- onclick="expandAllRows(this); event.preventDefault();">
10
- <%= pb_rails("icon", props: { icon: "arrows-from-line", cursor: "pointer", fixed_width: true, padding_right: "xs" }) %>
11
- </button>
12
- <% end %>
13
- <%= item[:label] %>
1
+ <%= pb_content_tag(:thead) do %>
2
+ <% object.header_rows.each_with_index do |header_row, row_index| %>
3
+ <%= pb_rails("table/table_row") do %>
4
+ <% header_row.each_with_index do |cell, cell_index| %>
5
+ <% header_id = cell[:accessor].present? ? cell[:accessor] : "header_#{row_index}_#{cell_index}" %>
6
+ <%= pb_rails("table/table_header", props: { id: header_id, colspan: cell[:colspan], classname: [object.th_classname, ('last-header-cell' if cell[:is_last_in_group] && cell_index != 0)].compact.join(' '), sort_menu: cell[:accessor] ? cell[:sort_menu] : nil }) do %>
7
+ <%= pb_rails("flex", props:{ align: "center", justify: cell_index.zero? ? "start" : row_index === header_rows.size - 1 ? "end" : "center", text_align: "end" }) do %>
8
+ <% if cell_index.zero? && (object.enable_toggle_expansion == "header" || object.enable_toggle_expansion == "all") && row_index === header_rows.size - 1 %>
9
+ <button
10
+ class="gray-icon toggle-all-icon"
11
+ onclick="expandAllRows(this); event.preventDefault();">
12
+ <%= pb_rails("icon", props: { icon: "arrows-from-line", cursor: "pointer", fixed_width: true, padding_right: "xs" }) %>
13
+ </button>
14
14
  <% end %>
15
+ <%= cell[:label] %>
15
16
  <% end %>
16
- <% end %>
17
17
  <% end %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
18
21
  <% end %>
@@ -16,6 +16,84 @@ module Playbook
16
16
  def th_classname
17
17
  generate_classname("table-header-cells", separator: " ")
18
18
  end
19
+
20
+ def header_rows
21
+ wrapped_columns = wrap_leaf_columns(column_definitions)
22
+
23
+ rows = []
24
+ max_depth = compute_max_depth(wrapped_columns)
25
+ max_depth.times { rows << [] }
26
+ process_columns(wrapped_columns, rows, 0, max_depth)
27
+ rows
28
+ end
29
+
30
+ private
31
+
32
+ def compute_max_depth(columns)
33
+ columns.map do |col|
34
+ col[:columns] ? 1 + compute_max_depth(col[:columns]) : 1
35
+ end.max || 1
36
+ end
37
+
38
+ def process_columns(columns, rows, current_depth, max_depth)
39
+ total_columns = columns.size
40
+ columns.each_with_index do |col, index|
41
+ is_last = index == total_columns - 1
42
+ if col[:columns]
43
+ colspan = compute_leaf_columns(col[:columns])
44
+ rows[current_depth] << {
45
+ label: col[:label],
46
+ colspan: colspan,
47
+ is_last_in_group: is_last && current_depth.positive?,
48
+ }
49
+
50
+ process_columns(col[:columns], rows, current_depth + 1, max_depth)
51
+ else
52
+ colspan = 1
53
+ rows[current_depth] << {
54
+ label: col[:label],
55
+ colspan: colspan,
56
+ accessor: col[:accessor],
57
+ sort_menu: col[:sort_menu],
58
+ is_last_in_group: is_last && current_depth.positive?,
59
+ }
60
+ end
61
+ end
62
+ end
63
+
64
+ def compute_leaf_columns(columns)
65
+ columns.reduce(0) do |sum, col|
66
+ col[:columns] ? sum + compute_leaf_columns(col[:columns]) : sum + 1
67
+ end
68
+ end
69
+
70
+ def wrap_leaf_columns(column_definitions)
71
+ max_depth = compute_max_depth(column_definitions)
72
+
73
+ column_definitions.map do |col|
74
+ if col.key?(:columns)
75
+ {
76
+ label: col[:label],
77
+ columns: wrap_leaf_columns(col[:columns]),
78
+ }
79
+ else
80
+ # For leaf columns, wrap with empty labels up to max depth to get proper structure
81
+ wrap_leaf_column(col, max_depth)
82
+ end
83
+ end
84
+ end
85
+
86
+ def wrap_leaf_column(col, max_depth)
87
+ wrapped = {
88
+ accessor: col[:accessor],
89
+ label: col[:label] || "",
90
+ sort_menu: col[:sort_menu] || nil,
91
+ }
92
+ (max_depth - 1).times do
93
+ wrapped = { label: "", columns: [wrapped] }
94
+ end
95
+ wrapped
96
+ end
19
97
  end
20
98
  end
21
99
  end
@@ -1,6 +1,7 @@
1
- <%= pb_content_tag do %>
1
+ <%= pb_content_tag(:tr) do %>
2
2
  <% object.column_definitions.each_with_index do |column, index| %>
3
- <%= pb_rails("table/table_cell", props: { tag:"div", classname:object.td_classname}) do %>
3
+ <% next unless column[:accessor].present? %>
4
+ <%= pb_rails("table/table_cell", props: { classname:object.td_classname(column)}) do %>
4
5
  <%= pb_rails("flex", props:{ align: "center", justify: index.zero? ? "start" : "end" }) do %>
5
6
  <% if collapsible_trail && index.zero? %>
6
7
  <% (1..depth).each do |i| %>
@@ -13,7 +14,7 @@
13
14
  <div style="padding-left: <%= depth * 1.25 %>em">
14
15
  <%= pb_rails("flex", props:{align: "center", column_gap: "xs"}) do %>
15
16
  <% if index.zero? && object.row[:children].present? %>
16
- <button id="<%= object.row.object_id.to_s + object.id %>" class="gray-icon expand-toggle-icon" data-advanced-table="true" >
17
+ <button id="<%= "#{object.id}_#{object.row.object_id}" %>" class="gray-icon expand-toggle-icon" data-advanced-table="true" >
17
18
  <%= pb_rails("icon", props: { id: "advanced-table_open_icon", icon: "circle-play", cursor: "pointer" }) %>
18
19
  <%= pb_rails("icon", props: { id: "advanced-table_close_icon", display: "none", icon: "circle-play", cursor: "pointer", rotation: 90 }) %>
19
20
  </button>
@@ -16,8 +16,10 @@ module Playbook
16
16
  generate_classname("pb_table_tr", "bg-white", subrow_depth_classname, separator: " ")
17
17
  end
18
18
 
19
- def td_classname
20
- generate_classname("id-cell", "chrome-styles", separator: " ")
19
+ def td_classname(column)
20
+ classes = %w[id-cell chrome-styles]
21
+ classes << "last-cell" if column[:is_last_in_group]
22
+ classes.join(" ")
21
23
  end
22
24
 
23
25
  def depth_accessors
@@ -29,6 +31,8 @@ module Playbook
29
31
  private
30
32
 
31
33
  def custom_renderer_value(column, index)
34
+ return nil unless column[:accessor].present?
35
+
32
36
  if index.zero?
33
37
  if depth.zero?
34
38
  row[column[:accessor].to_sym]
@@ -37,6 +41,7 @@ module Playbook
37
41
  key = item.to_sym
38
42
  return row[key] if depth - 1 == accessor_index
39
43
  end
44
+ nil
40
45
  end
41
46
  else
42
47
  row[column[:accessor].to_sym]
@@ -1,10 +1,6 @@
1
- <%= content_tag(:div,
2
- aria: object.aria,
3
- class: object.classname,
4
- data: object.data,
5
- id: object.id) do %>
1
+ <%= pb_content_tag(:div) do %>
6
2
  <% object.column_definitions.each_with_index do |column, index| %>
7
- <%= pb_rails("table/table_cell", props: { tag: "div", classname: object.td_classname}) do %>
3
+ <%= pb_rails("table/table_cell", props: { classname: object.td_classname}) do %>
8
4
  <%= pb_rails("flex", props:{ align: "center", justify: "start" }) do %>
9
5
  <% if collapsible_trail && index.zero? %>
10
6
  <% (1..depth).each do |i| %>
@@ -21,6 +21,14 @@
21
21
  border-width: 0px;
22
22
  }
23
23
 
24
+ @function ends-with($string, $suffix) {
25
+ $suffix-length: str-length($suffix);
26
+ @if $suffix-length == 0 {
27
+ @return true;
28
+ }
29
+ @return str-slice($string, -$suffix-length) == $suffix;
30
+ }
31
+
24
32
  [class^=pb_card_header_kit] {
25
33
  flex-grow: 0;
26
34
  flex-shrink: 0;
@@ -30,9 +38,19 @@
30
38
  border: 0;
31
39
  border-radius: $pb_card_header_border_radius $pb_card_header_border_radius 0px 0px;
32
40
  @each $color_name, $color_value in $pb_card_header_colors {
33
- &[class*=_#{"" + $color_name}] {
34
- @include pb_card_header_color($color_value);
35
- color: lightenText($color_value);
41
+ @if not ends-with($color_name, '_subtle') {
42
+ &[class*="_#{$color_name}"] {
43
+ @include pb_card_header_color($color_value);
44
+ color: lightenText($color_value);
45
+ }
46
+ }
47
+ }
48
+ @each $color_name, $color_value in $pb_card_header_colors {
49
+ @if ends-with($color_name, '_subtle') {
50
+ &[class*="_#{$color_name}"] {
51
+ @include pb_card_header_color($color_value);
52
+ color: lightenText($color_value);
53
+ }
36
54
  }
37
55
  }
38
56
  @each $color_name, $color_value in $pb_card_header_colors {
@@ -88,3 +88,21 @@
88
88
  Body
89
89
  <% end %>
90
90
  <% end %>
91
+
92
+ <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %>
93
+ <%= pb_rails("card/card_header", props: { padding: "sm", header_color: "success_subtle" }) do %>
94
+ <%= pb_rails("body", props: { text: "Success Subtle" }) %>
95
+ <% end %>
96
+ <%= pb_rails("card/card_body", props: { padding: "md" }) do %>
97
+ Body
98
+ <% end %>
99
+ <% end %>
100
+
101
+ <%= pb_rails("card", props: { padding: "none", header: true, margin_bottom: "sm"}) do %>
102
+ <%= pb_rails("card/card_header", props: { padding: "sm", header_color: "error_subtle" }) do %>
103
+ <%= pb_rails("body", props: { text: "Error Subtle"}) %>
104
+ <% end %>
105
+ <%= pb_rails("card/card_body", props: { padding: "md" }) do %>
106
+ Body
107
+ <% end %>
108
+ <% end %>
@@ -231,6 +231,46 @@ const CardHeader = (props) => {
231
231
  />
232
232
  </Card.Body>
233
233
  </Card>
234
+
235
+ <Card
236
+ {...props}
237
+ marginBottom='sm'
238
+ padding="none"
239
+ >
240
+ <Card.Header
241
+ headerColor="success_subtle"
242
+ >
243
+ <Body
244
+ text="Success Subtle"
245
+ />
246
+ </Card.Header>
247
+ <Card.Body>
248
+ <Body
249
+ {...props}
250
+ text="Body"
251
+ />
252
+ </Card.Body>
253
+ </Card>
254
+
255
+ <Card
256
+ {...props}
257
+ marginBottom='sm'
258
+ padding="none"
259
+ >
260
+ <Card.Header
261
+ headerColor="error_subtle"
262
+ >
263
+ <Body
264
+ text="Error Subtle"
265
+ />
266
+ </Card.Header>
267
+ <Card.Body>
268
+ <Body
269
+ {...props}
270
+ text="Body"
271
+ />
272
+ </Card.Body>
273
+ </Card>
234
274
  </>
235
275
  )
236
276
  }