playbook_ui 18.2.0.pre.rc.2 → 18.2.0

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_background/_background.tsx +3 -1
  3. data/app/pb_kits/playbook/pb_background/kit.schema.json +1 -0
  4. data/app/pb_kits/playbook/pb_caption/_caption.tsx +3 -1
  5. data/app/pb_kits/playbook/pb_caption/kit.schema.json +1 -0
  6. data/app/pb_kits/playbook/pb_container/_container.tsx +3 -1
  7. data/app/pb_kits/playbook/pb_container/kit.schema.json +1 -0
  8. data/app/pb_kits/playbook/pb_currency/_currency.tsx +3 -1
  9. data/app/pb_kits/playbook/pb_currency/kit.schema.json +1 -0
  10. data/app/pb_kits/playbook/pb_dashboard_value/_dashboard_value.tsx +3 -1
  11. data/app/pb_kits/playbook/pb_dashboard_value/kit.schema.json +1 -0
  12. data/app/pb_kits/playbook/pb_detail/_detail.tsx +3 -1
  13. data/app/pb_kits/playbook/pb_detail/kit.schema.json +1 -0
  14. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +6 -5
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom.md +7 -2
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom_rails.md +7 -2
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.html.erb +33 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.jsx +42 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.md +24 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.jsx +101 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.md +3 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +3 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -0
  24. data/app/pb_kits/playbook/pb_dropdown/index.js +19 -10
  25. data/app/pb_kits/playbook/pb_dropdown/quickpick/index.ts +60 -18
  26. data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +92 -3
  27. data/app/pb_kits/playbook/pb_dropdown/utilities/index.ts +8 -1
  28. data/app/pb_kits/playbook/pb_dropdown/utilities/quickpickDateSync.ts +137 -0
  29. data/app/pb_kits/playbook/pb_filter/Filter/InteractiveFilter.tsx +1 -1
  30. data/app/pb_kits/playbook/pb_highlight/_highlight.tsx +3 -1
  31. data/app/pb_kits/playbook/pb_highlight/kit.schema.json +1 -0
  32. data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +3 -1
  33. data/app/pb_kits/playbook/pb_icon_stat_value/kit.schema.json +1 -0
  34. data/app/pb_kits/playbook/pb_icon_value/_icon_value.tsx +3 -1
  35. data/app/pb_kits/playbook/pb_icon_value/kit.schema.json +1 -0
  36. data/app/pb_kits/playbook/pb_label_value/_label_value.tsx +3 -1
  37. data/app/pb_kits/playbook/pb_label_value/kit.schema.json +1 -0
  38. data/app/pb_kits/playbook/pb_map/docs/_map_default.md +3 -3
  39. data/app/pb_kits/playbook/pb_message/_message.tsx +3 -1
  40. data/app/pb_kits/playbook/pb_message/kit.schema.json +1 -0
  41. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +3 -1
  42. data/app/pb_kits/playbook/pb_multiple_users/kit.schema.json +1 -0
  43. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +4 -2
  44. data/app/pb_kits/playbook/pb_multiple_users_stacked/kit.schema.json +1 -0
  45. data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx +10 -3
  46. data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/Toolbar.tsx +16 -1
  47. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.json +2 -3
  48. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.overrides.json +2 -3
  49. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_attributes.jsx +1 -5
  50. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.jsx +1 -5
  51. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.md +2 -3
  52. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_focus.jsx +1 -5
  53. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_height.jsx +3 -13
  54. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_inline.jsx +1 -5
  55. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +2 -3
  56. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_markdown_support.jsx +1 -2
  57. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_min_height.jsx +3 -13
  58. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_preview.jsx +1 -5
  59. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -5
  60. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_simple.jsx +1 -5
  61. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_sticky.jsx +1 -5
  62. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_templates.jsx +1 -5
  63. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.jsx +3 -8
  64. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.md +1 -1
  65. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +2 -2
  66. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.test.js +2 -1
  67. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_advanced.test.js +7 -5
  68. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +15 -4
  69. data/app/pb_kits/playbook/pb_stat_value/_stat_value.tsx +3 -1
  70. data/app/pb_kits/playbook/pb_stat_value/kit.schema.json +1 -0
  71. data/app/pb_kits/playbook/pb_table/_table.tsx +70 -197
  72. data/app/pb_kits/playbook/pb_table/index.ts +53 -122
  73. data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +26 -3
  74. data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.test.ts +266 -0
  75. data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.ts +145 -0
  76. data/app/pb_kits/playbook/pb_title_count/_title_count.tsx +3 -1
  77. data/app/pb_kits/playbook/pb_title_count/kit.schema.json +1 -0
  78. data/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx +3 -1
  79. data/app/pb_kits/playbook/pb_title_detail/kit.schema.json +1 -0
  80. data/app/pb_kits/playbook/utilities/test/globalProps/onClick.test.js +237 -0
  81. data/dist/ai/all-schemas.json +16 -0
  82. data/dist/ai/forms.json +1 -1
  83. data/dist/ai/index.json +2 -2
  84. data/dist/ai/kits/background.schema.json +1 -0
  85. data/dist/ai/kits/caption.schema.json +1 -0
  86. data/dist/ai/kits/container.schema.json +1 -0
  87. data/dist/ai/kits/currency.schema.json +1 -0
  88. data/dist/ai/kits/dashboard_value.schema.json +1 -0
  89. data/dist/ai/kits/detail.schema.json +1 -0
  90. data/dist/ai/kits/highlight.schema.json +1 -0
  91. data/dist/ai/kits/icon_stat_value.schema.json +1 -0
  92. data/dist/ai/kits/icon_value.schema.json +1 -0
  93. data/dist/ai/kits/label_value.schema.json +1 -0
  94. data/dist/ai/kits/message.schema.json +1 -0
  95. data/dist/ai/kits/multiple_users.schema.json +1 -0
  96. data/dist/ai/kits/multiple_users_stacked.schema.json +1 -0
  97. data/dist/ai/kits/stat_value.schema.json +1 -0
  98. data/dist/ai/kits/title_count.schema.json +1 -0
  99. data/dist/ai/kits/title_detail.schema.json +1 -0
  100. data/dist/ai/playgrounds/rich_text_editor.json +1 -1
  101. data/dist/chunks/{_detail-BqqzPTNr.js → _detail-Bsvl694i.js} +2 -2
  102. data/dist/chunks/{_pb_line_graph-BGyCJjgG.js → _pb_line_graph-Du7xE9vl.js} +1 -1
  103. data/dist/chunks/_table-Bf6LGyL9.js +1 -0
  104. data/dist/chunks/_typeahead-w6S5Vx4b.js +1 -0
  105. data/dist/chunks/{globalProps-K1DCn3il.js → globalProps-B120NbSJ.js} +1 -1
  106. data/dist/chunks/{lib-7vm9BMqp.js → lib-Buo6zHEw.js} +1 -1
  107. data/dist/chunks/quickpickDateSync-DLDx4zce.js +1 -0
  108. data/dist/chunks/{time_picker_helper-BZutIzB3.js → time_picker_helper-CYGg0UQ0.js} +1 -1
  109. data/dist/chunks/vendor.js +2 -2
  110. data/dist/playbook-rails-react-bindings.js +1 -1
  111. data/dist/playbook-rails.js +1 -1
  112. data/dist/playbook.css +1 -1
  113. data/lib/playbook/version.rb +1 -1
  114. metadata +19 -13
  115. data/dist/chunks/_table-CEZU0qUK.js +0 -1
  116. data/dist/chunks/_typeahead-C7iPR5ZF.js +0 -1
@@ -32,6 +32,7 @@
32
32
  }
33
33
  },
34
34
  "globalProps": true,
35
+ "globalEventProps": true,
35
36
  "usage": {
36
37
  "react": {
37
38
  "import": "import { StatValue } from 'playbook-ui'",
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react'
1
+ import React, { useCallback, useEffect, useState } from 'react'
2
2
  import classnames from 'classnames'
3
3
  import { buildAriaProps, buildDataProps, buildHtmlProps } from '../utilities/props'
4
4
  import { globalProps, GlobalProps, globalInlineProps } from '../utilities/globalProps'
@@ -10,6 +10,10 @@ import {
10
10
  TableCell,
11
11
  } from "./subcomponents";
12
12
  import { addDataTitle } from './utilities/addDataTitle'
13
+ import {
14
+ applyStickyColumns,
15
+ scheduleStickyColumns,
16
+ } from './utilities/stickyColumns'
13
17
  import Card from '../pb_card/_card'
14
18
  import Flex from '../pb_flex/_flex'
15
19
  import Title from '../pb_title/_title'
@@ -102,7 +106,13 @@ const Table = (props: TableProps): React.ReactElement => {
102
106
  const outerPaddingCss = outerPadding ? `outer_padding_${spaceCssName}${outerPadding}` : ''
103
107
  const isTableTag = tag === 'table'
104
108
  const dynamicInlineProps = globalInlineProps(props)
105
- const stickyRightColumnReversed = stickyRightColumn.reverse()
109
+ const [tableNode, setTableNode] = useState<HTMLElement | null>(null)
110
+ const setTableRef = useCallback(
111
+ (node: HTMLTableElement | HTMLDivElement | null) => {
112
+ setTableNode((current) => (current === node ? current : node))
113
+ },
114
+ []
115
+ )
106
116
 
107
117
  const isFilterVariant = variant === 'withFilter'
108
118
  const effectiveContainer = isFilterVariant ? false : container
@@ -132,224 +142,87 @@ const Table = (props: TableProps): React.ReactElement => {
132
142
  )
133
143
 
134
144
  useEffect(() => {
135
- const handleStickyLeftColumns = () => {
136
- if (!stickyLeftColumn.length) return;
137
- let accumulatedWidth = 0;
138
-
139
- stickyLeftColumn.forEach((colId, index) => {
140
- const isLastColumn = index === stickyLeftColumn.length - 1;
141
- const header = document.querySelector(`th[data-sticky-id="${colId}"]`);
142
- const cells = document.querySelectorAll(`td[data-sticky-id="${colId}"]`);
143
-
144
- if (header) {
145
- header.classList.add('sticky');
146
- (header as HTMLElement).style.left = `${accumulatedWidth}px`;
147
-
148
- if (!isLastColumn) {
149
- header.classList.add('with-border-right');
150
- header.classList.remove('sticky-left-shadow');
151
- } else {
152
- header.classList.remove('with-border-right');
153
- header.classList.add('sticky-left-shadow');
154
- }
155
-
156
- const headerWidth = (header as HTMLElement).offsetWidth;
157
- accumulatedWidth += headerWidth;
158
-
159
- cells.forEach((cell) => {
160
- cell.classList.add('sticky');
161
- (cell as HTMLElement).style.left = `${accumulatedWidth - headerWidth}px`;
162
-
163
- if (!isLastColumn) {
164
- cell.classList.add('with-border-right');
165
- cell.classList.remove('sticky-left-shadow');
166
- } else {
167
- cell.classList.remove('with-border-right');
168
- cell.classList.add('sticky-left-shadow');
169
- }
170
- });
171
- }
172
- });
173
- };
174
-
175
- setTimeout(() => {
176
- handleStickyLeftColumns();
177
- }, 10);
178
-
179
- window.addEventListener('resize', handleStickyLeftColumns);
180
-
181
- return () => {
182
- window.removeEventListener('resize', handleStickyLeftColumns);
183
- };
184
- }, [stickyLeftColumn]);
185
-
186
- useEffect(() => {
187
- const handleStickyRightColumns = () => {
188
- if (!stickyRightColumn.length) return;
189
- let accumulatedWidth = 0;
190
-
191
- stickyRightColumnReversed.forEach((colId, index) => {
192
- const isLastColumn = index === stickyRightColumn.length - 1;
193
- const header = document.querySelector(`th[data-sticky-id="${colId}"]`);
194
- const cells = document.querySelectorAll(`td[data-sticky-id="${colId}"]`);
195
-
196
- if (header) {
197
- header.classList.add('sticky');
198
- (header as HTMLElement).style.right = `${accumulatedWidth}px`;
199
-
200
- if (!isLastColumn) {
201
- header.classList.add('with-border-left');
202
- header.classList.remove('sticky-right-shadow');
203
- } else {
204
- header.classList.remove('with-border-left');
205
- header.classList.add('sticky-right-shadow');
206
- }
207
-
208
- const headerWidth = (header as HTMLElement).offsetWidth;
209
- accumulatedWidth += headerWidth;
210
-
211
- cells.forEach((cell) => {
212
- cell.classList.add('sticky');
213
- (cell as HTMLElement).style.right = `${accumulatedWidth - headerWidth}px`;
214
-
215
- if (!isLastColumn) {
216
- cell.classList.add('with-border-left');
217
- cell.classList.remove('sticky-right-shadow');
218
- } else {
219
- cell.classList.remove('with-border-left');
220
- cell.classList.add('sticky-right-shadow');
221
- }
222
- });
223
- }
224
- });
225
- };
145
+ if (!tableNode) return
146
+ if (!stickyLeftColumn.length && !stickyRightColumn.length) return
147
+
148
+ const applyStickyOffsets = () => {
149
+ if (stickyLeftColumn.length) {
150
+ applyStickyColumns(tableNode, stickyLeftColumn, 'left')
151
+ }
152
+ if (stickyRightColumn.length) {
153
+ applyStickyColumns(tableNode, stickyRightColumn, 'right')
154
+ }
155
+ }
226
156
 
227
- setTimeout(() => {
228
- handleStickyRightColumns();
229
- }, 10);
157
+ const cancelSchedule = scheduleStickyColumns(applyStickyOffsets)
158
+ window.addEventListener('resize', applyStickyOffsets)
230
159
 
231
- window.addEventListener('resize', handleStickyRightColumns);
160
+ let resizeObserver: ResizeObserver | undefined
161
+ if (typeof ResizeObserver !== 'undefined') {
162
+ resizeObserver = new ResizeObserver(applyStickyOffsets)
163
+ resizeObserver.observe(tableNode)
164
+ }
232
165
 
233
166
  return () => {
234
- window.removeEventListener('resize', handleStickyRightColumns);
235
- };
236
- }, [stickyRightColumn]);
167
+ cancelSchedule()
168
+ window.removeEventListener('resize', applyStickyOffsets)
169
+ resizeObserver?.disconnect()
170
+ }
171
+ }, [tableNode, stickyLeftColumn, stickyRightColumn])
237
172
 
238
173
  useEffect(() => {
239
174
  addDataTitle()
240
175
  }, [])
241
176
 
177
+ const tableElementProps = {
178
+ ...ariaProps,
179
+ ...dataProps,
180
+ ...htmlProps,
181
+ className: classNames,
182
+ id,
183
+ style: dynamicInlineProps,
184
+ }
185
+
186
+ const renderTableElement = () => (
187
+ isTableTag ? (
188
+ <table
189
+ {...tableElementProps}
190
+ ref={setTableRef}
191
+ >
192
+ {children}
193
+ </table>
194
+ ) : (
195
+ <div
196
+ {...tableElementProps}
197
+ ref={setTableRef}
198
+ >
199
+ {children}
200
+ </div>
201
+ )
202
+ )
203
+
242
204
  // ------------ Default Table (non-filter variant rendering) ------------
243
205
  const renderTable = () => {
244
- const tableElement = responsive === 'scroll' ? (
245
- <div className='table-responsive-scroll'>
246
- {isTableTag ? (
247
- <table
248
- {...ariaProps}
249
- {...dataProps}
250
- {...htmlProps}
251
- className={classNames}
252
- id={id}
253
- style={dynamicInlineProps}
254
- >
255
- {children}
256
- </table>
257
- ) : (
258
- <div
259
- {...ariaProps}
260
- {...dataProps}
261
- {...htmlProps}
262
- className={classNames}
263
- id={id}
264
- style={dynamicInlineProps}
265
- >
266
- {children}
267
- </div>
268
- )}
269
- </div>
270
- ) : (
271
- isTableTag ? (
272
- <table
273
- {...ariaProps}
274
- {...dataProps}
275
- {...htmlProps}
276
- className={classNames}
277
- id={id}
278
- style={dynamicInlineProps}
279
- >
280
- {children}
281
- </table>
282
- ) : (
283
- <div
284
- {...ariaProps}
285
- {...dataProps}
286
- {...htmlProps}
287
- className={classNames}
288
- id={id}
289
- style={dynamicInlineProps}
290
- >
291
- {children}
206
+ if (responsive === 'scroll') {
207
+ return (
208
+ <div className='table-responsive-scroll'>
209
+ {renderTableElement()}
292
210
  </div>
293
211
  )
294
- )
212
+ }
295
213
 
296
- return tableElement
214
+ return renderTableElement()
297
215
  }
298
216
  // ------------ End Default Table (non-filter variant rendering) ------------
299
217
 
300
218
  // ------------ variant = 'withFilter' rendering ------------
301
219
  const renderCardVariant = () => {
302
- // Render table element
303
220
  const tableElement = responsive === 'scroll' ? (
304
221
  <div className='table-responsive-scroll'>
305
- {isTableTag ? (
306
- <table
307
- {...ariaProps}
308
- {...dataProps}
309
- {...htmlProps}
310
- className={classNames}
311
- id={id}
312
- style={dynamicInlineProps}
313
- >
314
- {children}
315
- </table>
316
- ) : (
317
- <div
318
- {...ariaProps}
319
- {...dataProps}
320
- {...htmlProps}
321
- className={classNames}
322
- id={id}
323
- style={dynamicInlineProps}
324
- >
325
- {children}
326
- </div>
327
- )}
222
+ {renderTableElement()}
328
223
  </div>
329
224
  ) : (
330
- isTableTag ? (
331
- <table
332
- {...ariaProps}
333
- {...dataProps}
334
- {...htmlProps}
335
- className={classNames}
336
- id={id}
337
- style={dynamicInlineProps}
338
- >
339
- {children}
340
- </table>
341
- ) : (
342
- <div
343
- {...ariaProps}
344
- {...dataProps}
345
- {...htmlProps}
346
- className={classNames}
347
- id={id}
348
- style={dynamicInlineProps}
349
- >
350
- {children}
351
- </div>
352
- )
225
+ renderTableElement()
353
226
  )
354
227
 
355
228
  // Default filter props that CAN be overridden (All props from Filter kit CAN be used, but these are the ones we set as defaults)
@@ -1,4 +1,8 @@
1
1
  import PbEnhancedElement from '../pb_enhanced_element'
2
+ import {
3
+ applyStickyColumns,
4
+ scheduleStickyColumns,
5
+ } from './utilities/stickyColumns'
2
6
 
3
7
  const TABLE_WRAPPER_SELECTOR = "[data-pb-table-wrapper]";
4
8
  const TABLE_COLLAPSIBLE_WRAPPER_SELECTOR = "[data-pb-table-collapsible-wrapper]";
@@ -7,7 +11,10 @@ const TABLE_COLLAPSIBLE_CELL_SELECTOR = "[data-pb-table-collapsible-cell-id]";
7
11
  export default class PbTable extends PbEnhancedElement {
8
12
  stickyLeftColumns: string[] = [];
9
13
  stickyRightColumns: string[] = [];
10
- stickyRightColumnsReversed: string[] = [];
14
+ private stickyTable: Element | null = null;
15
+ private cancelStickySchedule: (() => void) | null = null;
16
+ private stickyResizeObserver: ResizeObserver | null = null;
17
+ private boundApplyStickyOffsets = () => this.applyStickyOffsets();
11
18
 
12
19
  static get selector(): string {
13
20
  return TABLE_WRAPPER_SELECTOR;
@@ -31,133 +38,59 @@ export default class PbTable extends PbEnhancedElement {
31
38
  })
32
39
  }
33
40
 
34
- this.initStickyLeftColumns();
35
- this.initStickyRightColumns();
41
+ this.initStickyColumns();
36
42
  this.handleCollapsibleClick();
37
43
  this.handleCollapsibleRow();
38
44
  }
39
45
 
40
- initStickyLeftColumns() {
41
- const table = this.element.querySelector('.sticky-left-column');
42
-
43
- if (table) {
44
- const classList = Array.from(table.classList);
45
- const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-left-columns-ids-'));
46
-
47
- if (stickyColumnClass) {
48
- this.stickyLeftColumns = stickyColumnClass
49
- .replace('sticky-left-columns-ids-', '')
50
- .split('-');
51
-
52
- if (this.stickyLeftColumns.length > 0) {
53
- setTimeout(() => {
54
- this.handleStickyLeftColumns();
55
- window.addEventListener('resize', () => this.handleStickyLeftColumns());
56
- }, 10);
57
- }
58
- }
46
+ initStickyColumns() {
47
+ const table =
48
+ this.element.querySelector('.sticky-left-column, .sticky-right-column') ||
49
+ (this.element.classList.contains('sticky-left-column') ||
50
+ this.element.classList.contains('sticky-right-column')
51
+ ? this.element
52
+ : null);
53
+
54
+ if (!table) return;
55
+
56
+ this.stickyTable = table;
57
+ const classList = Array.from(table.classList);
58
+ const stickyLeftClass = classList.find(cls => cls.startsWith('sticky-left-columns-ids-'));
59
+ const stickyRightClass = classList.find(cls => cls.startsWith('sticky-right-columns-ids-'));
60
+
61
+ if (stickyLeftClass) {
62
+ this.stickyLeftColumns = stickyLeftClass
63
+ .replace('sticky-left-columns-ids-', '')
64
+ .split('-');
59
65
  }
60
- }
61
-
62
- handleStickyLeftColumns() {
63
- let accumulatedWidth = 0;
64
-
65
- this.stickyLeftColumns.forEach((colId, index) => {
66
- const isLastColumn = index === this.stickyLeftColumns.length - 1;
67
- const header = this.element.querySelector(`th[data-sticky-id="${colId}"]`);
68
- const cells = this.element.querySelectorAll(`td[data-sticky-id="${colId}"]`);
69
-
70
- if (header) {
71
- header.classList.add('sticky');
72
- (header as HTMLElement).style.left = `${accumulatedWidth}px`;
73
-
74
- if (!isLastColumn) {
75
- header.classList.add('with-border-right');
76
- header.classList.remove('sticky-left-shadow');
77
- } else {
78
- header.classList.remove('with-border-right');
79
- header.classList.add('sticky-left-shadow');
80
- }
81
-
82
- const headerWidth = (header as HTMLElement).offsetWidth;
83
- accumulatedWidth += headerWidth;
84
-
85
- cells.forEach((cell) => {
86
- cell.classList.add('sticky');
87
- (cell as HTMLElement).style.left = `${accumulatedWidth - headerWidth}px`;
88
-
89
- if (!isLastColumn) {
90
- cell.classList.add('with-border-right');
91
- cell.classList.remove('sticky-left-shadow');
92
- } else {
93
- cell.classList.remove('with-border-right');
94
- cell.classList.add('sticky-left-shadow');
95
- }
96
- });
97
- }
98
- });
99
- }
100
66
 
101
- initStickyRightColumns() {
102
- const table = this.element.querySelector('.sticky-right-column');
67
+ if (stickyRightClass) {
68
+ this.stickyRightColumns = stickyRightClass
69
+ .replace('sticky-right-columns-ids-', '')
70
+ .split('-');
71
+ }
103
72
 
104
- if (table) {
105
- const classList = Array.from(table.classList);
106
- const stickyColumnClass = classList.find(cls => cls.startsWith('sticky-right-columns-ids-'));
73
+ if (!this.stickyLeftColumns.length && !this.stickyRightColumns.length) return;
107
74
 
108
- if (stickyColumnClass) {
109
- this.stickyRightColumns = stickyColumnClass
110
- .replace('sticky-right-columns-ids-', '')
111
- .split('-');
112
- this.stickyRightColumnsReversed = this.stickyRightColumns.reverse();
75
+ this.cancelStickySchedule = scheduleStickyColumns(this.boundApplyStickyOffsets);
76
+ window.addEventListener('resize', this.boundApplyStickyOffsets);
113
77
 
114
- if (this.stickyRightColumns.length > 0) {
115
- setTimeout(() => {
116
- this.handleStickyRightColumns();
117
- window.addEventListener('resize', () => this.handleStickyRightColumns());
118
- }, 10);
119
- }
120
- }
78
+ if (typeof ResizeObserver !== 'undefined') {
79
+ this.stickyResizeObserver = new ResizeObserver(this.boundApplyStickyOffsets);
80
+ this.stickyResizeObserver.observe(table);
121
81
  }
122
82
  }
123
83
 
124
- handleStickyRightColumns() {
125
- let accumulatedWidth = 0;
126
-
127
- this.stickyRightColumnsReversed.forEach((colId, index) => {
128
- const isLastColumn = index === this.stickyRightColumns.length - 1;
129
- const header = this.element.querySelector(`th[data-sticky-id="${colId}"]`);
130
- const cells = this.element.querySelectorAll(`td[data-sticky-id="${colId}"]`);
131
-
132
- if (header) {
133
- header.classList.add('sticky');
134
- (header as HTMLElement).style.right = `${accumulatedWidth}px`;
135
-
136
- if (!isLastColumn) {
137
- header.classList.add('with-border-left');
138
- header.classList.remove('sticky-right-shadow');
139
- } else {
140
- header.classList.remove('with-border-right');
141
- header.classList.add('sticky-right-shadow');
142
- }
143
-
144
- const headerWidth = (header as HTMLElement).offsetWidth;
145
- accumulatedWidth += headerWidth;
84
+ applyStickyOffsets() {
85
+ if (!this.stickyTable) return;
146
86
 
147
- cells.forEach((cell) => {
148
- cell.classList.add('sticky');
149
- (cell as HTMLElement).style.right = `${accumulatedWidth - headerWidth}px`;
87
+ if (this.stickyLeftColumns.length) {
88
+ applyStickyColumns(this.stickyTable, this.stickyLeftColumns, 'left');
89
+ }
150
90
 
151
- if (!isLastColumn) {
152
- cell.classList.add('with-border-left');
153
- cell.classList.remove('sticky-right-shadow');
154
- } else {
155
- cell.classList.remove('with-border-left');
156
- cell.classList.add('sticky-right-shadow');
157
- }
158
- });
159
- }
160
- });
91
+ if (this.stickyRightColumns.length) {
92
+ applyStickyColumns(this.stickyTable, this.stickyRightColumns, 'right');
93
+ }
161
94
  }
162
95
 
163
96
  handleCollapsibleClick() {
@@ -228,14 +161,12 @@ export default class PbTable extends PbEnhancedElement {
228
161
  }
229
162
  }
230
163
 
231
- // Cleanup method to remove event listener
232
164
  disconnect() {
233
- if (this.stickyLeftColumns.length > 0) {
234
- window.removeEventListener('resize', () => this.handleStickyLeftColumns());
235
- }
236
-
237
- if (this.stickyRightColumns.length > 0) {
238
- window.removeEventListener('resize', () => this.handleStickyRightColumns());
239
- }
165
+ this.cancelStickySchedule?.();
166
+ this.cancelStickySchedule = null;
167
+ window.removeEventListener('resize', this.boundApplyStickyOffsets);
168
+ this.stickyResizeObserver?.disconnect();
169
+ this.stickyResizeObserver = null;
170
+ this.stickyTable = null;
240
171
  }
241
172
  }
@@ -1,13 +1,38 @@
1
1
  @import "../../tokens/colors";
2
2
 
3
3
  [class^="pb_table"] {
4
+ // Collapsed borders + sticky columns leave subpixel seams at some zoom/sizes.
5
+ // Separate borders keep sticky offsets aligned with painted cell edges.
6
+ &.table-sm,
7
+ &.table-md,
8
+ &.table-lg {
9
+ &.sticky-left-column,
10
+ &.sticky-right-column {
11
+ border-collapse: separate;
12
+ border-spacing: 0;
13
+ }
14
+ }
15
+
4
16
  .sticky {
5
17
  position: sticky !important;
6
18
  z-index: 1;
7
19
  background-color: white;
20
+ background-clip: padding-box;
21
+ }
22
+
23
+ // Corner cells (sticky header + sticky column) must sit above scrolling
24
+ // header siblings in multi-row theads.
25
+ &.sticky-header {
26
+ thead .sticky,
27
+ .pb_table_thead .sticky {
28
+ z-index: 2;
29
+ }
8
30
  }
9
31
 
10
- // For use with sticky left columns
32
+ // For use with sticky left columns.
33
+ // Default left/right keep single sticky edge (and Nitro colspan corner cells
34
+ // that use these classes without data-sticky-id) pinned. Multi-column JS
35
+ // offsets override via inline style.
11
36
  .sticky-left-shadow {
12
37
  box-shadow: 4px 0 10px rgba(60, 106, 172, 0.16) !important;
13
38
  left: 0;
@@ -15,7 +40,6 @@
15
40
 
16
41
  .with-border-right {
17
42
  border-right: 1px solid var(--table-border-color, #{$border_light}) !important;
18
- left: 0;
19
43
  }
20
44
 
21
45
  // For use with sticky right columns
@@ -26,6 +50,5 @@
26
50
 
27
51
  .with-border-left {
28
52
  border-left: 1px solid var(--table-border-color, #{$border_light}) !important;
29
- right: 0;
30
53
  }
31
54
  }