playbook_ui 14.11.1 → 14.12.0.pre.alpha.PBNTR720railscarddraggable5649

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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +2 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +18 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +27 -5
  5. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +17 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +23 -1
  7. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +29 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +61 -4
  9. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +1 -1
  10. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +2 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.jsx +50 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination.jsx +1 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_with_props.jsx +1 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows.jsx +60 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows.md +5 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions.jsx +78 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions.md +1 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header.jsx +53 -0
  19. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header.md +1 -0
  20. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows.jsx +52 -0
  21. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_no_subrows.md +1 -0
  22. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sort_control.md +2 -2
  23. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +33 -0
  24. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_rails.md +1 -0
  25. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_no_subrows.json +42 -0
  26. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_with_id.json +299 -0
  27. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +6 -0
  28. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +6 -1
  29. data/app/pb_kits/playbook/pb_button/button.html.erb +2 -3
  30. data/app/pb_kits/playbook/pb_card/card.html.erb +21 -2
  31. data/app/pb_kits/playbook/pb_card/card.rb +7 -0
  32. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +1 -6
  33. data/app/pb_kits/playbook/pb_collapsible/collapsible.html.erb +3 -1
  34. data/app/pb_kits/playbook/pb_collapsible/collapsible.rb +3 -0
  35. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +24 -16
  36. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -0
  37. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default_date.md +1 -1
  38. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_turbo_frames.html.erb +13 -0
  39. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_turbo_frames_rails.md +3 -0
  40. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +1 -0
  41. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.html.erb +1 -3
  42. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.md +7 -0
  43. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.html.erb +3 -9
  44. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.md +5 -0
  45. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.html.erb +38 -0
  46. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.md +3 -0
  47. data/app/pb_kits/playbook/pb_draggable/docs/example.yml +1 -0
  48. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +145 -183
  49. data/app/pb_kits/playbook/pb_drawer/_drawer.tsx +158 -268
  50. data/app/pb_kits/playbook/pb_drawer/context.ts +11 -0
  51. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_behavior.jsx +38 -0
  52. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +3 -45
  53. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +0 -1
  54. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_default.jsx +9 -16
  55. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +44 -19
  56. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.md +21 -3
  57. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +16 -21
  58. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_sizes.jsx +2 -19
  59. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +2 -1
  60. data/app/pb_kits/playbook/pb_drawer/docs/index.js +1 -0
  61. data/app/pb_kits/playbook/pb_drawer/drawer.test.jsx +5 -5
  62. data/app/pb_kits/playbook/pb_drawer/hooks/useBreakpoint.tsx +60 -0
  63. data/app/pb_kits/playbook/pb_drawer/hooks/useDrawerAnimation.tsx +21 -0
  64. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.md +1 -1
  65. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +7 -12
  66. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +9 -14
  67. data/app/pb_kits/playbook/pb_dropdown/dropdown_option.html.erb +6 -11
  68. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +8 -14
  69. data/app/pb_kits/playbook/pb_icon_button/_icon_button.scss +78 -0
  70. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_default.html.erb +3 -0
  71. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +7 -0
  72. data/app/pb_kits/playbook/pb_icon_button/icon_button.html.erb +16 -0
  73. data/app/pb_kits/playbook/pb_icon_button/icon_button.rb +22 -0
  74. data/app/pb_kits/playbook/pb_list/item.html.erb +30 -8
  75. data/app/pb_kits/playbook/pb_list/item.rb +7 -0
  76. data/app/pb_kits/playbook/pb_list/list.html.erb +31 -11
  77. data/app/pb_kits/playbook/pb_list/list.rb +4 -0
  78. data/app/pb_kits/playbook/pb_loading_inline/_loading_inline.tsx +6 -1
  79. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +4 -0
  80. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +1 -0
  81. data/app/pb_kits/playbook/pb_multiple_users/multiple_users.html.erb +1 -1
  82. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +30 -12
  83. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.html.erb +15 -0
  84. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.jsx +24 -0
  85. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.md +1 -0
  86. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
  87. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  88. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
  89. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +20 -1
  90. data/app/pb_kits/playbook/pb_radio/_radio.scss +12 -8
  91. data/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.jsx +8 -3
  92. data/app/pb_kits/playbook/pb_select/_select.scss +3 -5
  93. data/app/pb_kits/playbook/pb_select/_select.tsx +5 -1
  94. data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
  95. data/app/pb_kits/playbook/pb_selectable_icon/_selectable_icon.tsx +9 -1
  96. data/app/pb_kits/playbook/pb_selectable_icon/docs/_selectable_icon_default.jsx +4 -1
  97. data/app/pb_kits/playbook/pb_selectable_icon/docs/_selectable_icon_single_select.jsx +4 -1
  98. data/app/pb_kits/playbook/pb_selectable_list/selectable_list.html.erb +17 -3
  99. data/app/pb_kits/playbook/pb_selectable_list/selectable_list.rb +3 -0
  100. data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.html.erb +11 -4
  101. data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.rb +3 -0
  102. data/app/pb_kits/playbook/pb_table/_table.tsx +2 -3
  103. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_columns.html.erb +74 -0
  104. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_columns_rails.md +3 -0
  105. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns_rails.md +2 -2
  106. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_right_columns.html.erb +74 -0
  107. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_right_columns_rails.md +3 -0
  108. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible.html.erb +47 -0
  109. data/app/pb_kits/playbook/pb_table/docs/_table_with_collapsible_rails.md +2 -0
  110. data/app/pb_kits/playbook/pb_table/docs/example.yml +3 -0
  111. data/app/pb_kits/playbook/pb_table/index.ts +187 -88
  112. data/app/pb_kits/playbook/pb_table/styles/_collapsible.scss +12 -0
  113. data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +6 -5
  114. data/app/pb_kits/playbook/pb_table/table.html.erb +1 -1
  115. data/app/pb_kits/playbook/pb_table/table.rb +17 -2
  116. data/app/pb_kits/playbook/pb_table/table_row.html.erb +20 -1
  117. data/app/pb_kits/playbook/pb_table/table_row.rb +5 -0
  118. data/app/pb_kits/playbook/pb_table/utilities/addDataTitle.ts +22 -0
  119. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_mask.html.erb +46 -0
  120. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_mask_rails.md +3 -0
  121. data/app/pb_kits/playbook/pb_text_input/docs/example.yml +2 -1
  122. data/app/pb_kits/playbook/pb_text_input/index.js +103 -0
  123. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +4 -0
  124. data/app/pb_kits/playbook/pb_text_input/text_input.rb +33 -3
  125. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +30 -30
  126. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -2
  127. data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +12 -4
  128. data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +5 -1
  129. data/app/pb_kits/playbook/pb_typeahead/components/IndicatorsContainer.tsx +8 -3
  130. data/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx +6 -1
  131. data/app/pb_kits/playbook/pb_typeahead/components/Option.tsx +21 -6
  132. data/app/pb_kits/playbook/pb_typeahead/components/Placeholder.tsx +13 -6
  133. data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +7 -3
  134. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx +2 -0
  135. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +19 -0
  136. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +27 -0
  137. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.md +1 -0
  138. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async.jsx +4 -2
  139. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_pills_async_custom_options.jsx +5 -5
  140. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  141. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  142. data/dist/chunks/_typeahead-BWwaAo_0.js +36 -0
  143. data/dist/chunks/_weekday_stacked-zyBCd1s8.js +45 -0
  144. data/dist/chunks/lazysizes-B7xYodB-.js +1 -0
  145. data/dist/chunks/{lib-B7sgJtGS.js → lib-kMuhBuU7.js} +2 -2
  146. data/dist/chunks/{pb_form_validation-C5Cc0-1v.js → pb_form_validation-DBJ0wZuS.js} +1 -1
  147. data/dist/chunks/vendor.js +1 -1
  148. data/dist/menu.yml +6 -0
  149. data/dist/playbook-doc.js +1 -1
  150. data/dist/playbook-rails-react-bindings.js +1 -1
  151. data/dist/playbook-rails.js +1 -1
  152. data/dist/playbook.css +1 -1
  153. data/lib/playbook/version.rb +2 -2
  154. metadata +66 -9
  155. data/dist/chunks/_typeahead-C2iCBqxQ.js +0 -36
  156. data/dist/chunks/_weekday_stacked-E-5KcEkc.js +0 -45
  157. data/dist/chunks/lazysizes-DHz07jlL.js +0 -1
  158. /data/app/pb_kits/playbook/pb_advanced_table/docs/{_advanced_table_table_props.md → _advanced_table_table_props_react.md} +0 -0
  159. /data/app/pb_kits/playbook/pb_table/docs/{_table_with_collapsible.md → _table_with_collapsible_react.md} +0 -0
@@ -1,108 +1,207 @@
1
1
  import PbEnhancedElement from '../pb_enhanced_element'
2
2
 
3
- export default class PbTable extends PbEnhancedElement {
4
- private stickyLeftColumns: string[] = [];
5
- private handleStickyLeftColumnsRef: () => void;
6
-
7
- static get selector(): string {
8
- return '.table-responsive-collapse'
9
- }
3
+ const TABLE_WRAPPER_SELECTOR = "[data-pb-table-wrapper]";
4
+ const TABLE_COLLAPSIBLE_WRAPPER_SELECTOR = "[data-pb-table-collapsible-wrapper]";
10
5
 
11
- connect(): void {
12
- const tables = document.querySelectorAll('.table-responsive-collapse');
13
- // Each Table
14
- [].forEach.call(tables, (table: HTMLTableElement) => {
15
- // Header Titles
16
- const headers: string[] = [];
17
- [].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => {
18
- const colSpan = header.colSpan
19
- for (let i = 0; i < colSpan; i++) {
20
- headers.push(header.textContent.replace(/\r?\n|\r/, ''));
21
- }
22
- });
23
- // for each row in tbody
24
- [].forEach.call(table.querySelectorAll('tbody tr'), (row: HTMLTableRowElement) => {
25
- // for each cell
26
- [].forEach.call(row.cells, (cell: HTMLTableCellElement, headerIndex: number) => {
27
- // apply the attribute
28
- cell.setAttribute('data-title', headers[headerIndex])
29
- })
30
- })
6
+ export default class PbTable extends PbEnhancedElement {
7
+ stickyLeftColumns: string[] = [];
8
+ stickyRightColumns: string[] = [];
9
+ stickyRightColumnsReversed: string[] = [];
10
+
11
+ static get selector(): string {
12
+ return TABLE_WRAPPER_SELECTOR;
13
+ }
14
+
15
+ connect() {
16
+ if (this.element.classList.contains('table-responsive-collapse')) {
17
+ const headers: string[] = [];
18
+
19
+ [].forEach.call(this.element.querySelectorAll('th'), (header: HTMLTableCellElement) => {
20
+ const colSpan = header.colSpan
21
+ for (let i = 0; i < colSpan; i++) {
22
+ headers.push(header.textContent.replace(/\r?\n|\r/, ''));
23
+ }
31
24
  });
32
25
 
33
- // New sticky columns logic
34
- this.initStickyLeftColumns();
26
+ [].forEach.call(this.element.querySelectorAll('tbody tr'), (row: HTMLTableRowElement) => {
27
+ [].forEach.call(row.cells, (cell: HTMLTableCellElement, headerIndex: number) => {
28
+ cell.setAttribute('data-title', headers[headerIndex])
29
+ })
30
+ })
35
31
  }
36
32
 
37
- private initStickyLeftColumns(): void {
38
- // Find tables with sticky-left-column class
39
- const tables = document.querySelectorAll('.sticky-left-column');
40
-
41
- tables.forEach((table) => {
42
- // Extract sticky left column IDs by looking at the component's class
43
- const classList = Array.from(table.classList);
44
-
45
- // Look for classes in the format sticky-left-column-{ids}
46
- const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-columns-'));
47
- if (stickyColumnClass) {
48
- // Extract the IDs from the class name
49
- this.stickyLeftColumns = stickyColumnClass
50
- .replace('sticky-columns-', '')
51
- .split('-');
52
-
53
- if (this.stickyLeftColumns.length > 0) {
54
- setTimeout(() => {
55
- this.handleStickyLeftColumnsRef = this.handleStickyLeftColumns.bind(this);
56
- this.handleStickyLeftColumns();
57
- window.addEventListener('resize', this.handleStickyLeftColumnsRef);
58
- }, 10);
59
- }
33
+ this.initStickyLeftColumns();
34
+ this.initStickyRightColumns();
35
+ this.handleCollapsibleClick();
36
+ this.handleCollapsibleRow();
37
+ }
38
+
39
+ initStickyLeftColumns() {
40
+ const table = this.element.querySelector('.sticky-left-column');
41
+
42
+ if (table) {
43
+ const classList = Array.from(table.classList);
44
+ const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-left-columns-ids-'));
45
+
46
+ if (stickyColumnClass) {
47
+ this.stickyLeftColumns = stickyColumnClass
48
+ .replace('sticky-left-columns-ids-', '')
49
+ .split('-');
50
+
51
+ if (this.stickyLeftColumns.length > 0) {
52
+ setTimeout(() => {
53
+ this.handleStickyLeftColumns();
54
+ window.addEventListener('resize', () => this.handleStickyLeftColumns());
55
+ }, 10);
60
56
  }
61
- });
57
+ }
62
58
  }
59
+ }
60
+
61
+ handleStickyLeftColumns() {
62
+ let accumulatedWidth = 0;
63
+
64
+ this.stickyLeftColumns.forEach((colId, index) => {
65
+ const isLastColumn = index === this.stickyLeftColumns.length - 1;
66
+ const header = this.element.querySelector(`th[id="${colId}"]`);
67
+ const cells = this.element.querySelectorAll(`td[id="${colId}"]`);
68
+
69
+ if (header) {
70
+ header.classList.add('sticky');
71
+ (header as HTMLElement).style.left = `${accumulatedWidth}px`;
72
+
73
+ if (!isLastColumn) {
74
+ header.classList.add('with-border-right');
75
+ header.classList.remove('sticky-left-shadow');
76
+ } else {
77
+ header.classList.remove('with-border-right');
78
+ header.classList.add('sticky-left-shadow');
79
+ }
63
80
 
64
- private handleStickyLeftColumns(): void {
65
- let accumulatedWidth = 0;
66
-
67
- this.stickyLeftColumns.forEach((colId, index) => {
68
- const isLastColumn = index === this.stickyLeftColumns.length - 1;
69
- const header = document.querySelector(`th[id="${colId}"]`);
70
- const cells = document.querySelectorAll(`td[id="${colId}"]`);
71
-
72
- if (header) {
73
- header.classList.add('sticky');
74
- (header as HTMLElement).style.left = `${accumulatedWidth}px`;
81
+ accumulatedWidth += (header as HTMLElement).offsetWidth;
82
+ }
75
83
 
76
- if (!isLastColumn) {
77
- header.classList.add('with-border-right');
78
- header.classList.remove('sticky-left-shadow');
79
- } else {
80
- header.classList.remove('with-border-right');
81
- header.classList.add('sticky-left-shadow');
82
- }
84
+ cells.forEach((cell) => {
85
+ cell.classList.add('sticky');
86
+ (cell as HTMLElement).style.left = `${accumulatedWidth - (header as HTMLElement).offsetWidth}px`;
83
87
 
84
- accumulatedWidth += (header as HTMLElement).offsetWidth;
88
+ if (!isLastColumn) {
89
+ cell.classList.add('with-border-right');
90
+ cell.classList.remove('sticky-left-shadow');
91
+ } else {
92
+ cell.classList.remove('with-border-right');
93
+ cell.classList.add('sticky-left-shadow');
85
94
  }
95
+ });
96
+ });
97
+ }
98
+
99
+ initStickyRightColumns() {
100
+ const table = this.element.querySelector('.sticky-right-column');
101
+
102
+ if (table) {
103
+ const classList = Array.from(table.classList);
104
+ const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-right-columns-ids-'));
105
+
106
+ if (stickyColumnClass) {
107
+ this.stickyRightColumns = stickyColumnClass
108
+ .replace('sticky-right-columns-ids-', '')
109
+ .split('-');
110
+ this.stickyRightColumnsReversed = this.stickyRightColumns.reverse();
111
+
112
+ if (this.stickyRightColumns.length > 0) {
113
+ setTimeout(() => {
114
+ this.handleStickyRightColumns();
115
+ window.addEventListener('resize', () => this.handleStickyRightColumns());
116
+ }, 10);
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ handleStickyRightColumns() {
123
+ let accumulatedWidth = 0;
124
+
125
+ this.stickyRightColumnsReversed.forEach((colId, index) => {
126
+ const isLastColumn = index === this.stickyRightColumns.length - 1;
127
+ const header = this.element.querySelector(`th[id="${colId}"]`);
128
+ const cells = this.element.querySelectorAll(`td[id="${colId}"]`);
129
+
130
+ if (header) {
131
+ header.classList.add('sticky');
132
+ (header as HTMLElement).style.right = `${accumulatedWidth}px`;
133
+
134
+ if (!isLastColumn) {
135
+ header.classList.add('with-border-left');
136
+ header.classList.remove('sticky-right-shadow');
137
+ } else {
138
+ header.classList.remove('with-border-right');
139
+ header.classList.add('sticky-right-shadow');
140
+ }
141
+
142
+ accumulatedWidth += (header as HTMLElement).offsetWidth;
143
+ }
86
144
 
87
- cells.forEach((cell) => {
88
- cell.classList.add('sticky');
89
- (cell as HTMLElement).style.left = `${accumulatedWidth - (header as HTMLElement).offsetWidth}px`;
145
+ cells.forEach((cell) => {
146
+ cell.classList.add('sticky');
147
+ (cell as HTMLElement).style.right = `${accumulatedWidth - (header as HTMLElement).offsetWidth}px`;
90
148
 
91
- if (!isLastColumn) {
92
- cell.classList.add('with-border-right');
93
- cell.classList.remove('sticky-left-shadow');
94
- } else {
95
- cell.classList.remove('with-border-right');
96
- cell.classList.add('sticky-left-shadow');
97
- }
149
+ if (!isLastColumn) {
150
+ cell.classList.add('with-border-left');
151
+ cell.classList.remove('sticky-right-shadow');
152
+ } else {
153
+ cell.classList.remove('with-border-left');
154
+ cell.classList.add('sticky-right-shadow');
155
+ }
156
+ });
157
+ });
158
+ }
159
+
160
+ handleCollapsibleClick() {
161
+ const collapsibleElements = this.element.querySelectorAll(TABLE_COLLAPSIBLE_WRAPPER_SELECTOR);
162
+ collapsibleElements.forEach((collapsibleElement) => {
163
+ collapsibleElement.addEventListener('click', (event) => {
164
+ document.dispatchEvent(new CustomEvent(`collapsed-toggle${(event.currentTarget as HTMLElement).id}`))
165
+
166
+ const toggleElements = this.element.querySelectorAll(`.collapsible_border_toggle${(event.currentTarget as HTMLElement).id}`);
167
+ toggleElements.forEach(element => {
168
+ element.classList.toggle('no-border');
169
+ element.classList.toggle('border-active');
98
170
  });
171
+ })
172
+ })
173
+ }
174
+
175
+ handleCollapsibleRow() {
176
+ const collapsibleRows = this.element.querySelectorAll('.pb_table_collapsible_row');
177
+ if (collapsibleRows.length > 0) {
178
+ collapsibleRows.forEach((row) => {
179
+ const previousRow = row.previousElementSibling;
180
+
181
+ if (
182
+ previousRow &&
183
+ previousRow.tagName === 'TR'
184
+ ) {
185
+ const tdCount = previousRow.querySelectorAll('td').length;
186
+ const collapsibleTd = row.querySelector('td');
187
+ if (collapsibleTd) {
188
+ collapsibleTd.colSpan = tdCount;
189
+ }
190
+ } else {
191
+ return
192
+ }
99
193
  });
100
194
  }
195
+ }
101
196
 
102
- // Cleanup method to remove event listener
103
- disconnect(): void {
104
- if (this.handleStickyLeftColumnsRef) {
105
- window.removeEventListener('resize', this.handleStickyLeftColumnsRef);
106
- }
197
+ // Cleanup method to remove event listener
198
+ disconnect() {
199
+ if (this.stickyLeftColumns.length > 0) {
200
+ window.removeEventListener('resize', () => this.handleStickyLeftColumns());
201
+ }
202
+
203
+ if (this.stickyRightColumns.length > 0) {
204
+ window.removeEventListener('resize', () => this.handleStickyRightColumns());
107
205
  }
108
- }
206
+ }
207
+ }
@@ -32,4 +32,16 @@
32
32
  }
33
33
  }
34
34
  }
35
+
36
+ .collapsible-tr {
37
+ cursor: pointer;
38
+ }
39
+
40
+ .no-border {
41
+ border-bottom: none !important;
42
+ }
43
+
44
+ .border-active {
45
+ border-bottom: 1px;
46
+ }
35
47
  }
@@ -1,7 +1,7 @@
1
1
  @import "../../tokens/screen_sizes";
2
+ @import "../../tokens/border_radius";
2
3
 
3
4
  .table-responsive-scroll {
4
- display: block;
5
5
  overflow-x: scroll;
6
6
 
7
7
  // hides duplicate scroll bar for those that see two (byproduct of repeated table-responsive-scroll class
@@ -27,11 +27,12 @@
27
27
  // Responsive Styles
28
28
  @media (max-width: 1600px) {
29
29
  &[class*="table-responsive-scroll"] {
30
- border-radius: 4px;
31
- box-shadow: 1px 0 0 0px $border_light,
30
+ &:has(> table.table-card) {
31
+ border-radius: $border_rad_light;
32
+ box-shadow: 1px 0 0 0px $border_light,
32
33
  -1px 0 0 0px $border_light
33
- }
34
-
34
+ }
35
+ }
35
36
  &[class^=pb_table].table-sm.table-card thead tr th:first-child,
36
37
  &[class^=pb_table].table-sm:not(.no-hover).table-card tbody tr td:first-child {
37
38
  border-left-width: 0px;
@@ -4,7 +4,7 @@
4
4
  <% responsive_class = nil %>
5
5
  <% end %>
6
6
 
7
- <%= content_tag(:div, class: responsive_class) do %>
7
+ <%= content_tag(:div, class: responsive_class, 'data-pb-table-wrapper' => true) do %>
8
8
  <% if object.tag == "table" %>
9
9
  <%= content_tag(:table,
10
10
  aria: object.aria,
@@ -25,6 +25,8 @@ module Playbook
25
25
  default: false
26
26
  prop :sticky_left_column, type: Playbook::Props::Array,
27
27
  default: []
28
+ prop :sticky_right_column, type: Playbook::Props::Array,
29
+ default: []
28
30
  prop :vertical_border, type: Playbook::Props::Boolean,
29
31
  default: false
30
32
  prop :striped, type: Playbook::Props::Boolean,
@@ -40,7 +42,7 @@ module Playbook
40
42
  generate_classname(
41
43
  "pb_table", "table-#{size}", single_line_class, dark_class,
42
44
  disable_hover_class, container_class, data_table_class, sticky_class, sticky_left_column_class,
43
- collapse_class, vertical_border_class, striped_class, outer_padding_class,
45
+ sticky_right_column_class, collapse_class, vertical_border_class, striped_class, outer_padding_class,
44
46
  "table-responsive-#{responsive}", separator: " "
45
47
  )
46
48
  end
@@ -83,7 +85,7 @@ module Playbook
83
85
  if sticky_left_column.empty?
84
86
  nil
85
87
  else
86
- sticky_col_classname = "sticky-left-column sticky-columns"
88
+ sticky_col_classname = "sticky-left-column sticky-left-columns-ids"
87
89
  sticky_left_column.each do |id|
88
90
  sticky_col_classname += "-#{id}"
89
91
  end
@@ -92,6 +94,19 @@ module Playbook
92
94
  end
93
95
  end
94
96
 
97
+ def sticky_right_column_class
98
+ if sticky_right_column.empty?
99
+ nil
100
+ else
101
+ sticky_col_classname = "sticky-right-column sticky-right-columns-ids"
102
+ sticky_right_column.each do |id|
103
+ sticky_col_classname += "-#{id}"
104
+ end
105
+
106
+ sticky_col_classname
107
+ end
108
+ end
109
+
95
110
  def striped_class
96
111
  striped ? "striped" : nil
97
112
  end
@@ -1,4 +1,23 @@
1
- <% if object.tag == "table" %>
1
+ <% if object.collapsible && object.tag == "table" %>
2
+ <%= content_tag(:tr,
3
+ aria: object.aria,
4
+ class: object.classname + " collapsible-tr",
5
+ data: object.data.merge(id: object.id),
6
+ id: object.id,
7
+ 'data-pb-table-collapsible-wrapper' => true,
8
+ **combined_html_options) do %>
9
+ <%= content.presence %>
10
+ <% end %>
11
+
12
+ <tr class="pb_table_collapsible_row">
13
+ <%= pb_rails("collapsible", props: { classname: "collapsible_border_toggle#{object.id}" + " no-border", name: "default-example", tag: "td", padding: "none" }) do %>
14
+ <%= pb_rails("flex", props: { data: { "collapsible-main": "true"} }) %>
15
+ <%= pb_rails("collapsible/collapsible_content", props: { classname: object.collapsible_side_highlight ? "table_collapsible_side_highlight" : "", padding: "none", margin: "none", id: "collapsed-toggle#{object.id}" }) do %>
16
+ <%= object.collapsible_content %>
17
+ <% end %>
18
+ <% end %>
19
+ </tr>
20
+ <% elsif object.tag == "table" %>
2
21
  <%= content_tag(:tr,
3
22
  aria: object.aria,
4
23
  class: object.classname,
@@ -8,6 +8,11 @@ module Playbook
8
8
  prop :tag, type: Playbook::Props::Enum,
9
9
  values: %w[table div],
10
10
  default: "table"
11
+ prop :collapsible, type: Playbook::Props::Boolean,
12
+ default: false
13
+ prop :collapsible_content
14
+ prop :collapsible_side_highlight, type: Playbook::Props::Boolean,
15
+ default: false
11
16
 
12
17
  def classname
13
18
  generate_classname("pb_table_row_kit", side_highlight_class) + tag_class
@@ -0,0 +1,22 @@
1
+ export const addDataTitle = () => {
2
+ const tables = document.querySelectorAll('.table-responsive-collapse');
3
+ // Each Table
4
+ [].forEach.call(tables, (table: HTMLTableElement) => {
5
+ // Header Titles
6
+ const headers: string[] = [];
7
+ [].forEach.call(table.querySelectorAll('th'), (header: HTMLTableCellElement) => {
8
+ const colSpan = header.colSpan
9
+ for (let i = 0; i < colSpan; i++) {
10
+ headers.push(header.textContent.replace(/\r?\n|\r/, ''));
11
+ }
12
+ });
13
+ // for each row in tbody
14
+ [].forEach.call(table.querySelectorAll('tbody tr'), (row: HTMLTableRowElement) => {
15
+ // for each cell
16
+ [].forEach.call(row.cells, (cell: HTMLTableCellElement, headerIndex: number) => {
17
+ // apply the attribute
18
+ cell.setAttribute('data-title', headers[headerIndex])
19
+ })
20
+ })
21
+ });
22
+ }
@@ -0,0 +1,46 @@
1
+ <%= pb_rails("text_input", props: {
2
+ label: "Currency",
3
+ mask: "currency",
4
+ margin_bottom: "md",
5
+ name: "currency_name",
6
+ placeholder:"$0.00"
7
+ }) %>
8
+
9
+ <%= pb_rails("text_input", props: {
10
+ label: "ZIP Code",
11
+ mask: "zip_code",
12
+ margin_bottom: "md",
13
+ placeholder: "12345"
14
+ }) %>
15
+
16
+ <%= pb_rails("text_input", props: {
17
+ label: "Postal Code",
18
+ mask: "postal_code",
19
+ placeholder: "12345-6789",
20
+ margin_bottom: "md",
21
+ }) %>
22
+
23
+ <%= pb_rails("text_input", props: {
24
+ label: "SSN",
25
+ mask: "ssn",
26
+ margin_bottom: "md",
27
+ placeholder: "123-45-6789"
28
+ }) %>
29
+
30
+ <%= pb_rails("title" , props: {
31
+ text: "Hidden Input Under The Hood",
32
+ padding_bottom: "sm"
33
+ })%>
34
+
35
+ <%= pb_rails("text_input", props: {
36
+ label: "Currency",
37
+ mask: "currency",
38
+ margin_bottom: "md",
39
+ name: "currency_name",
40
+ id: "example-currency",
41
+ placeholder: "$0.00",
42
+ }) %>
43
+
44
+ <style>
45
+ #example-currency-sanitized {display: flex !important;}
46
+ </style>
@@ -0,0 +1,3 @@
1
+ The mask prop lets you style your inputs while maintaining the value that the user typed in.
2
+
3
+ It uses a hidden input field to submit the unformatted value as it will have the proper `name` attribute. It will also copy the id field with a `"#{your-id-sanitized}"`
@@ -8,6 +8,7 @@ examples:
8
8
  - text_input_inline: Inline
9
9
  - text_input_no_label: No Label
10
10
  - text_input_options: Input Options
11
+ - text_input_mask: Mask
11
12
  react:
12
13
  - text_input_default: Default
13
14
  - text_input_error: With Error
@@ -23,4 +24,4 @@ examples:
23
24
  - text_input_error_swift: With Error
24
25
  - text_input_disabled_swift: Disabled
25
26
  - text_input_add_on_swift: Add On
26
- - text_input_props_swift: ""
27
+ - text_input_props_swift: ""
@@ -0,0 +1,103 @@
1
+ export default class PbTextInput {
2
+ static start() {
3
+ const inputElements = document.querySelectorAll('[data-pb-input-mask="true"]');
4
+
5
+ inputElements.forEach((inputElement) => {
6
+ inputElement.addEventListener("input", (event) => {
7
+ const maskType = inputElement.getAttribute("mask");
8
+ const cursorPosition = inputElement.selectionStart;
9
+
10
+ let rawValue = event.target.value;
11
+ let formattedValue = rawValue;
12
+
13
+ // Apply formatting based on the mask type
14
+ switch (maskType) {
15
+ case "currency":
16
+ formattedValue = formatCurrency(rawValue);
17
+ break;
18
+ case "ssn":
19
+ formattedValue = formatSSN(rawValue);
20
+ break;
21
+ case "postal_code":
22
+ formattedValue = formatPostalCode(rawValue);
23
+ break;
24
+ case "zip_code":
25
+ formattedValue = formatZipCode(rawValue);
26
+ break;
27
+ }
28
+
29
+ // Update the sanitized input field in the same wrapper
30
+ const sanitizedInput = inputElement
31
+ .closest(".text_input_wrapper")
32
+ ?.querySelector('[data="sanitized-pb-input"]');
33
+
34
+ if (sanitizedInput) {
35
+ switch (maskType) {
36
+ case "ssn":
37
+ sanitizedInput.value = sanitizeSSN(formattedValue);
38
+ break;
39
+ case "currency":
40
+ sanitizedInput.value = sanitizeCurrency(formattedValue);
41
+ break;
42
+ default:
43
+ sanitizedInput.value = formattedValue;
44
+ }
45
+ }
46
+
47
+ inputElement.value = formattedValue;
48
+ setCursorPosition(inputElement, cursorPosition, rawValue, formattedValue);
49
+ });
50
+ });
51
+
52
+ }
53
+ }
54
+
55
+ function formatCurrency(value) {
56
+ const numericValue = value.replace(/[^0-9]/g, "").slice(0, 15);
57
+
58
+ if (!numericValue) return "";
59
+
60
+ const dollars = parseFloat((parseInt(numericValue) / 100).toFixed(2));
61
+ if (dollars === 0) return "";
62
+
63
+ return new Intl.NumberFormat("en-US", {
64
+ style: "currency",
65
+ currency: "USD",
66
+ maximumFractionDigits: 2,
67
+ }).format(dollars);
68
+ }
69
+
70
+ function formatSSN(value) {
71
+ const cleaned = value.replace(/\D/g, "").slice(0, 9);
72
+ return cleaned
73
+ .replace(/(\d{5})(?=\d)/, "$1-")
74
+ .replace(/(\d{3})(?=\d)/, "$1-");
75
+ }
76
+
77
+ function formatZipCode(value) {
78
+ return value.replace(/\D/g, "").slice(0, 5);
79
+ }
80
+
81
+ function formatPostalCode(value) {
82
+ const cleaned = value.replace(/\D/g, "").slice(0, 9);
83
+ return cleaned.replace(/(\d{5})(?=\d)/, "$1-");
84
+ }
85
+
86
+ function sanitizeSSN(input) {
87
+ return input.replace(/\D/g, "");
88
+ }
89
+
90
+ function sanitizeCurrency(input) {
91
+ return input.replace(/[$,]/g, "");
92
+ }
93
+
94
+ // function to set cursor position
95
+ function setCursorPosition(inputElement, cursorPosition, rawValue, formattedValue) {
96
+ const difference = formattedValue.length - rawValue.length;
97
+
98
+ const newPosition = Math.max(0, cursorPosition + difference);
99
+
100
+ requestAnimationFrame(() => {
101
+ inputElement.setSelectionRange(newPosition, newPosition);
102
+ });
103
+ }
@@ -13,9 +13,13 @@
13
13
  <%= pb_rails("text_input/add_on", props: object.add_on_props) do %>
14
14
  <%= input_tag %>
15
15
  <% end %>
16
+ <% elsif mask.present? %>
17
+ <%= input_tag %>
18
+ <%= tag(:input, data: "sanitized-pb-input", id: sanitized_id, name: object.name, style: "display: none;") %>
16
19
  <% else %>
17
20
  <%= input_tag %>
18
21
  <% end %>
19
22
  <%= pb_rails("body", props: {dark: object.dark, status: "negative", text: object.error}) if object.error %>
20
23
  <% end %>
21
24
  <% end %>
25
+