@jetbrains/ring-ui 8.0.0-beta.1 → 8.0.0-beta.10

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 (199) hide show
  1. package/babel.config.js +1 -9
  2. package/components/alert/alert-actions.d.ts +4 -0
  3. package/components/alert/alert-actions.js +5 -0
  4. package/components/alert/alert-heading.d.ts +4 -0
  5. package/components/alert/alert-heading.js +6 -0
  6. package/components/alert/alert.css +63 -22
  7. package/components/alert/alert.d.ts +14 -1
  8. package/components/alert/alert.js +34 -19
  9. package/components/alert/container.css +1 -2
  10. package/components/alert-service/alert-service.d.ts +2 -1
  11. package/components/alert-service/alert-service.js +8 -4
  12. package/components/auth/auth-core.d.ts +5 -9
  13. package/components/auth/auth-core.js +56 -15
  14. package/components/auth-dialog-service/auth-dialog-service.js +2 -2
  15. package/components/avatar/avatar-info.js +0 -2
  16. package/components/avatar/avatar-size.d.ts +0 -4
  17. package/components/avatar/avatar-size.js +0 -4
  18. package/components/avatar/avatar.js +0 -6
  19. package/components/avatar/fallback-avatar.js +0 -28
  20. package/components/avatar-stack/avatar-stack.js +1 -1
  21. package/components/banner/banner.css +3 -3
  22. package/components/button/button.css +4 -4
  23. package/components/button/button.d.ts +0 -4
  24. package/components/button/button.js +2 -7
  25. package/components/button-group/button-group.css +5 -5
  26. package/components/checkbox/checkbox.d.ts +1 -1
  27. package/components/collapse/collapse-content.d.ts +7 -0
  28. package/components/collapse/collapse-content.js +44 -11
  29. package/components/collapse/collapse-context.d.ts +2 -0
  30. package/components/collapse/collapse-context.js +2 -0
  31. package/components/collapse/collapse-control.js +2 -2
  32. package/components/collapse/collapse.css +8 -2
  33. package/components/collapse/collapse.d.ts +9 -0
  34. package/components/collapse/collapse.js +12 -3
  35. package/components/{expand → collapsible-group}/collapsible-group.css +7 -1
  36. package/components/collapsible-group/collapsible-group.d.ts +33 -0
  37. package/components/{expand → collapsible-group}/collapsible-group.js +13 -21
  38. package/components/confirm-service/confirm-service.js +2 -2
  39. package/components/data-list/data-list.d.ts +1 -1
  40. package/components/data-list/data-list.mock.d.ts +1 -1
  41. package/components/data-list/item.d.ts +1 -1
  42. package/components/data-list/selection.d.ts +1 -1
  43. package/components/data-list/selection.js +1 -1
  44. package/components/date-picker/consts.d.ts +1 -0
  45. package/components/date-picker/date-input.js +6 -2
  46. package/components/date-picker/months.js +2 -2
  47. package/components/date-picker/use-scroll-behavior.js +5 -6
  48. package/components/date-picker/years.js +2 -2
  49. package/components/dialog/dialog.d.ts +2 -2
  50. package/components/dialog/dialog.js +2 -2
  51. package/components/dropdown-menu/dropdown-menu.d.ts +4 -4
  52. package/components/dropdown-menu/dropdown-menu.js +4 -4
  53. package/components/editable-heading/editable-heading.d.ts +1 -2
  54. package/components/editable-heading/editable-heading.js +18 -19
  55. package/components/global/compose-refs.d.ts +2 -1
  56. package/components/global/compose-refs.js +24 -8
  57. package/components/global/create-stateful-context.js +5 -5
  58. package/components/global/focus-with-temporary-tabindex.d.ts +11 -0
  59. package/components/global/focus-with-temporary-tabindex.js +21 -0
  60. package/components/global/intersection-observer-context.d.ts +31 -11
  61. package/components/global/intersection-observer-context.js +47 -28
  62. package/components/global/is-within-interactive-element.d.ts +6 -0
  63. package/components/global/is-within-interactive-element.js +26 -0
  64. package/components/global/is-within-navigable-element.d.ts +6 -0
  65. package/components/global/is-within-navigable-element.js +27 -0
  66. package/components/global/parse-css-duration.d.ts +5 -0
  67. package/components/global/parse-css-duration.js +13 -0
  68. package/components/global/rerender-hoc.d.ts +4 -2
  69. package/components/global/rerender-hoc.js +4 -4
  70. package/components/global/schedule-with-cleanup.d.ts +12 -0
  71. package/components/global/schedule-with-cleanup.js +34 -0
  72. package/components/{legacy-table/selection.d.ts → global/table-selection.d.ts} +14 -14
  73. package/components/{legacy-table/selection.js → global/table-selection.js} +2 -2
  74. package/components/global/theme.d.ts +4 -3
  75. package/components/global/theme.js +8 -8
  76. package/components/global/variables.css +18 -28
  77. package/components/global/variables.interface.d.ts +0 -4
  78. package/components/global/variables_dark.css +18 -25
  79. package/components/header/header.css +3 -3
  80. package/components/http/http.d.ts +2 -2
  81. package/components/http/http.js +2 -2
  82. package/components/i18n/i18n-context.js +1 -1
  83. package/components/i18n/i18n.d.ts +1 -0
  84. package/components/i18n/messages.json +1 -0
  85. package/components/icon/icon.css +0 -27
  86. package/components/icon/icon.d.ts +0 -1
  87. package/components/icon/icon.js +2 -4
  88. package/components/input/input.d.ts +1 -1
  89. package/components/island/adaptive-island-hoc.js +4 -4
  90. package/components/island/content.d.ts +7 -2
  91. package/components/island/content.js +5 -5
  92. package/components/legacy-table/cell.js +1 -1
  93. package/components/legacy-table/header-cell.js +1 -1
  94. package/components/legacy-table/header.js +1 -1
  95. package/components/legacy-table/multitable.d.ts +1 -1
  96. package/components/legacy-table/row.d.ts +1 -1
  97. package/components/legacy-table/row.js +1 -1
  98. package/components/legacy-table/selection-adapter.d.ts +3 -3
  99. package/components/legacy-table/selection-shortcuts-hoc.d.ts +5 -5
  100. package/components/legacy-table/simple-table.d.ts +2 -2
  101. package/components/legacy-table/simple-table.js +3 -3
  102. package/components/legacy-table/smart-table.d.ts +5 -5
  103. package/components/legacy-table/smart-table.js +3 -3
  104. package/components/legacy-table/table.js +1 -1
  105. package/components/list/consts.d.ts +0 -1
  106. package/components/list/consts.js +0 -1
  107. package/components/list/list.d.ts +0 -4
  108. package/components/login-dialog/service.js +2 -2
  109. package/components/popup/popup.d.ts +2 -0
  110. package/components/popup/popup.js +2 -2
  111. package/components/popup/popup.target.d.ts +3 -2
  112. package/components/popup/popup.target.js +4 -6
  113. package/components/query-assist/query-assist.d.ts +3 -1
  114. package/components/query-assist/query-assist.js +2 -2
  115. package/components/radio/radio-item.d.ts +3 -3
  116. package/components/radio/radio-item.js +3 -4
  117. package/components/radio/radio.d.ts +2 -2
  118. package/components/radio/radio.js +1 -1
  119. package/components/select/select-popup.d.ts +3 -2
  120. package/components/select/select-popup.js +3 -3
  121. package/components/select/select.d.ts +6 -2
  122. package/components/select/select.js +2 -4
  123. package/components/selection-toolbar/selection-toolbar.css +86 -0
  124. package/components/selection-toolbar/selection-toolbar.d.ts +11 -0
  125. package/components/selection-toolbar/selection-toolbar.js +23 -0
  126. package/components/slider/slider.js +4 -5
  127. package/components/tab-trap/tab-trap.d.ts +3 -3
  128. package/components/tab-trap/tab-trap.js +3 -5
  129. package/components/table/default-item-renderer.d.ts +41 -11
  130. package/components/table/default-item-renderer.js +60 -47
  131. package/components/table/internal/reorder-animation-context.d.ts +21 -0
  132. package/components/table/internal/reorder-animation-context.js +60 -0
  133. package/components/table/internal/reorder-handle.d.ts +9 -0
  134. package/components/table/internal/reorder-handle.js +334 -0
  135. package/components/table/internal/reorder-layout-context.d.ts +16 -0
  136. package/components/table/internal/reorder-layout-context.js +69 -0
  137. package/components/table/internal/table-header.d.ts +1 -0
  138. package/components/table/internal/table-header.js +143 -0
  139. package/components/table/internal/virtualization.d.ts +41 -0
  140. package/components/table/{table-virtualize.js → internal/virtualization.js} +82 -46
  141. package/components/table/item-virtualization.d.ts +37 -0
  142. package/components/table/item-virtualization.js +26 -0
  143. package/components/table/reorder-animation.d.ts +37 -0
  144. package/components/table/reorder-animation.js +11 -0
  145. package/components/table/reorder-item-layout.d.ts +32 -0
  146. package/components/table/reorder-item-layout.js +23 -0
  147. package/components/table/table-const.d.ts +8 -6
  148. package/components/table/table-const.js +7 -5
  149. package/components/table/table-primitives.d.ts +64 -0
  150. package/components/table/table-primitives.js +34 -0
  151. package/components/table/table-props.d.ts +304 -0
  152. package/components/table/table-props.js +1 -0
  153. package/components/table/table.css +179 -67
  154. package/components/table/table.d.ts +231 -224
  155. package/components/table/table.js +327 -2
  156. package/components/tag/tag.css +3 -3
  157. package/components/tags-input/tags-input.d.ts +3 -1
  158. package/components/tooltip/tooltip.d.ts +2 -1
  159. package/components/tooltip/tooltip.js +6 -5
  160. package/components/upload/upload.d.ts +4 -3
  161. package/components/upload/upload.js +3 -7
  162. package/components/user-agreement/service.js +2 -2
  163. package/components/user-card/smart-user-card-tooltip.d.ts +1 -1
  164. package/components/util-stories.d.ts +26 -0
  165. package/components/util-stories.js +61 -0
  166. package/package.json +44 -47
  167. package/components/content-layout/content-layout.css +0 -109
  168. package/components/content-layout/content-layout.d.ts +0 -20
  169. package/components/content-layout/content-layout.js +0 -40
  170. package/components/content-layout/sidebar.d.ts +0 -26
  171. package/components/content-layout/sidebar.js +0 -71
  172. package/components/date-picker/use-intersection-observer.d.ts +0 -6
  173. package/components/date-picker/use-intersection-observer.js +0 -48
  174. package/components/expand/collapsible-group.d.ts +0 -16
  175. package/components/global/composeRefs.d.ts +0 -6
  176. package/components/global/composeRefs.js +0 -7
  177. package/components/global/use-event-callback.d.ts +0 -1
  178. package/components/global/use-event-callback.js +0 -15
  179. package/components/grid/col.d.ts +0 -16
  180. package/components/grid/col.js +0 -35
  181. package/components/grid/grid.css +0 -920
  182. package/components/grid/grid.d.ts +0 -13
  183. package/components/grid/grid.js +0 -19
  184. package/components/grid/row.d.ts +0 -21
  185. package/components/grid/row.js +0 -41
  186. package/components/old-browsers-message/__mocks__/old-browsers-message.js +0 -1
  187. package/components/old-browsers-message/old-browsers-message-stop.d.ts +0 -1
  188. package/components/old-browsers-message/old-browsers-message-stop.js +0 -2
  189. package/components/old-browsers-message/old-browsers-message.css +0 -26
  190. package/components/old-browsers-message/old-browsers-message.d.ts +0 -5
  191. package/components/old-browsers-message/old-browsers-message.js +0 -86
  192. package/components/old-browsers-message/white-list.d.ts +0 -2
  193. package/components/old-browsers-message/white-list.js +0 -24
  194. package/components/table/table-base.d.ts +0 -24
  195. package/components/table/table-base.js +0 -79
  196. package/components/table/table-component.d.ts +0 -53
  197. package/components/table/table-component.js +0 -101
  198. package/components/table/table-virtualize.d.ts +0 -32
  199. /package/components/legacy-table/{table.css → legacy-table.css} +0 -0
@@ -0,0 +1,86 @@
1
+ @import '../global/variables.css';
2
+
3
+ .toolbar {
4
+ display: inline-flex;
5
+ align-items: center;
6
+
7
+ box-sizing: border-box;
8
+ max-width: 100%;
9
+ height: calc(var(--ring-unit) * 5);
10
+ padding: 10px calc(var(--ring-unit) * 2);
11
+
12
+ pointer-events: auto;
13
+
14
+ color: var(--ring-white-text-color);
15
+ border-radius: var(--ring-border-radius-large);
16
+ background-color: var(--ring-sidebar-background-color);
17
+ box-shadow:
18
+ 0 2px 8px rgb(0 28 54 / 10%),
19
+ 0 1px 2px rgb(0 28 54 / 4%);
20
+
21
+ line-height: var(--ring-line-height);
22
+ gap: calc(var(--ring-unit) * 2);
23
+ }
24
+
25
+ .content {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: calc(var(--ring-unit) * 3);
29
+ }
30
+
31
+ .selectAll {
32
+ flex: none;
33
+ }
34
+
35
+ .actions {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: calc(var(--ring-unit) * 2);
39
+
40
+ min-width: 0;
41
+ }
42
+
43
+ .closeAction {
44
+ flex: none;
45
+ }
46
+
47
+ .separator {
48
+ flex: none;
49
+
50
+ width: 1px;
51
+ height: calc(var(--ring-unit) * 2);
52
+ margin-inline: var(--ring-unit);
53
+
54
+ background-color: var(--ring-line-color);
55
+ }
56
+
57
+ .compact {
58
+ min-width: 0;
59
+ max-width: 100%;
60
+ padding-inline: 12px;
61
+ }
62
+
63
+ .compact .content {
64
+ min-width: 0;
65
+ gap: var(--ring-unit);
66
+ }
67
+
68
+ .compact .label {
69
+ overflow: hidden;
70
+
71
+ flex: 1 1 auto;
72
+
73
+ min-width: 0;
74
+
75
+ white-space: nowrap;
76
+
77
+ text-overflow: ellipsis;
78
+ }
79
+
80
+ .compact .actions {
81
+ flex-shrink: 1;
82
+ }
83
+
84
+ .compact .separator {
85
+ margin-inline: 2px;
86
+ }
@@ -0,0 +1,11 @@
1
+ import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ export interface SelectionToolbarProps extends ComponentPropsWithRef<'div'> {
3
+ label: ReactNode;
4
+ selectAll?: ReactNode;
5
+ closeAction: ReactNode;
6
+ compact?: boolean;
7
+ 'data-test'?: string | null;
8
+ }
9
+ export declare function SelectionToolbar({ ref, label, selectAll, closeAction, compact, className, children, 'data-test': dataTest, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...restProps }: SelectionToolbarProps): import("react").JSX.Element;
10
+ export declare function SelectionToolbarSeparator(): import("react").JSX.Element;
11
+ export default SelectionToolbar;
@@ -0,0 +1,23 @@
1
+ import { useId } from 'react';
2
+ import classNames from 'classnames';
3
+ import dataTests from '../global/data-tests';
4
+ import Theme, { ThemeProvider } from '../global/theme';
5
+ import styles from './selection-toolbar.css';
6
+ export function SelectionToolbar({ ref, label, selectAll, closeAction, compact, className, children, 'data-test': dataTest, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...restProps }) {
7
+ const labelId = useId();
8
+ const hasSelectAll = selectAll != null;
9
+ return (<ThemeProvider {...restProps} ref={ref} theme={Theme.DARK} role='group' aria-label={ariaLabel} aria-labelledby={ariaLabelledBy ?? (ariaLabel == null ? labelId : undefined)} data-test={dataTests('ring-selection-toolbar', dataTest)} className={classNames(styles.toolbar, { [styles.compact]: compact }, className)}>
10
+ <div className={styles.content}>
11
+ <span id={labelId} className={styles.label}>
12
+ {label}
13
+ </span>
14
+ {hasSelectAll && <span className={styles.selectAll}>{selectAll}</span>}
15
+ <div className={styles.actions}>{children}</div>
16
+ </div>
17
+ <div className={styles.closeAction}>{closeAction}</div>
18
+ </ThemeProvider>);
19
+ }
20
+ export function SelectionToolbarSeparator() {
21
+ return <span role='separator' aria-orientation='vertical' className={styles.separator}/>;
22
+ }
23
+ export default SelectionToolbar;
@@ -1,11 +1,10 @@
1
- import { Fragment, useEffect, useRef, useState } from 'react';
1
+ import { Fragment, useEffect, useEffectEvent, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import classNames from 'classnames';
4
4
  import { isArray } from '../global/typescript-utils';
5
5
  import Shortcuts from '../shortcuts/shortcuts';
6
6
  import getUID from '../global/get-uid';
7
7
  import { adjustValues, calculateMarks, calculateValue, HUNDRED, toPercent, toRange, validateValue } from './slider.utils';
8
- import useEventCallback from '../global/use-event-callback';
9
8
  import styles from './slider.css';
10
9
  export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1, disabled, marks, showTicks, showTag, className, renderTag, onChange, }) => {
11
10
  const ref = useRef(null);
@@ -86,7 +85,7 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
86
85
  setIsDragging(true);
87
86
  previouslyDragged.current = false;
88
87
  };
89
- const handleMouseUp = useEventCallback(({ pageX }) => {
88
+ const handleMouseUp = useEffectEvent(({ pageX }) => {
90
89
  const nextValues = adjustValues(validValues, ref, draggedIndex, pageX, max, min, validStep);
91
90
  if (nextValues[0] > nextValues[1]) {
92
91
  nextValues.reverse();
@@ -96,7 +95,7 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
96
95
  setIsDragging(false);
97
96
  previouslyDragged.current = true;
98
97
  });
99
- const handleMouseMove = useEventCallback(({ pageX }) => {
98
+ const handleMouseMove = useEffectEvent(({ pageX }) => {
100
99
  const nextValues = adjustValues(validValues, ref, draggedIndex, pageX, max, min, validStep);
101
100
  if (nextValues[0] > nextValues[1]) {
102
101
  nextValues.reverse();
@@ -120,7 +119,7 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
120
119
  window.removeEventListener('mousemove', handleMouseMove);
121
120
  window.removeEventListener('mouseup', handleMouseUp);
122
121
  };
123
- }, [isDragging, disabled, handleMouseMove, handleMouseUp]);
122
+ }, [isDragging, disabled]);
124
123
  return (<div ref={ref} role='presentation' // contains interactive elements
125
124
  className={classNames(styles.slider, className, {
126
125
  [styles.disabled]: disabled,
@@ -2,6 +2,7 @@ import { type HTMLAttributes, type ReactNode } from 'react';
2
2
  import * as React from 'react';
3
3
  export declare const FOCUSABLE_ELEMENTS = "input, button, select, textarea, a[href], *[tabindex]:not([data-trap-button]):not([data-scrollable-container])";
4
4
  export interface TabTrapProps extends HTMLAttributes<HTMLElement> {
5
+ ref?: React.Ref<TabTrapObject>;
5
6
  children: ReactNode;
6
7
  trapDisabled?: boolean;
7
8
  autoFocusFirst?: boolean;
@@ -11,8 +12,7 @@ export interface TabTrapProps extends HTMLAttributes<HTMLElement> {
11
12
  /**
12
13
  * @name TabTrap
13
14
  */
14
- interface TabTrap {
15
+ export interface TabTrapObject {
15
16
  node: HTMLElement | null;
16
17
  }
17
- declare const TabTrap: React.ForwardRefExoticComponent<TabTrapProps & React.RefAttributes<TabTrap>>;
18
- export default TabTrap;
18
+ export default function TabTrap({ ref, children, trapDisabled, autoFocusFirst, focusBackOnClose, focusBackOnExit, ...restProps }: TabTrapProps): React.JSX.Element;
@@ -1,10 +1,9 @@
1
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, } from 'react';
1
+ import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
2
2
  import * as React from 'react';
3
3
  import { isNodeInVisiblePartOfPage } from '../global/dom';
4
4
  import styles from './tab-trap.css';
5
5
  export const FOCUSABLE_ELEMENTS = 'input, button, select, textarea, a[href], *[tabindex]:not([data-trap-button]):not([data-scrollable-container])';
6
- // eslint-disable-next-line no-shadow
7
- const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, autoFocusFirst = true, focusBackOnClose = true, focusBackOnExit = false, ...restProps }, ref) {
6
+ export default function TabTrap({ ref, children, trapDisabled = false, autoFocusFirst = true, focusBackOnClose = true, focusBackOnExit = false, ...restProps }) {
8
7
  const nodeRef = useRef(null);
9
8
  const trapButtonNodeRef = useRef(null);
10
9
  const previousFocusedNodeRef = useRef(null);
@@ -121,5 +120,4 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
121
120
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
122
121
  tabIndex={0} onFocus={restoreFocusOrFocusFirst} data-trap-button/>
123
122
  </div>);
124
- });
125
- export default TabTrap;
123
+ }
@@ -1,25 +1,55 @@
1
- import { type ComponentPropsWithoutRef } from 'react';
1
+ import { type ComponentPropsWithRef } from 'react';
2
2
  export interface DefaultItemRendererProps {
3
3
  /**
4
- * Installed on the `<tr>` element
4
+ * The index of the `data` item to render.
5
5
  */
6
- ref?: React.RefObject<HTMLTableRowElement | null>;
6
+ index: number;
7
7
  /**
8
- * The index of the `data` item to render
8
+ * If `true`, the row will be focusable with up/down arrow keys.
9
+ * Focus is implemented using the
10
+ * ["roving tabindex"](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Guides/Keyboard-navigable_JavaScript_widgets#technique_1_roving_tabindex)
11
+ * technique, that is, only the focused row has `tabIndex={0}`.
9
12
  */
10
- index: number;
13
+ keyboardFocusable?: boolean;
11
14
  /**
12
- * Changes the highlight on hover and applies the pointer cursor.
13
- * Note that `false` doesn't mean it cannot handle `onClick`.
15
+ * Changes the background on hover and applies the pointer cursor.
16
+ * Note that `false` does not mean the row cannot handle `onClick`.
14
17
  */
15
18
  clickable?: boolean;
16
19
  /**
17
- * A level of a nested item. Results in an indent for columns with `indent: true`.
18
- * 0, negative and not set mean no indent.
20
+ * If `true`, the row is highlighted as selected with a different background color.
21
+ */
22
+ selected?: boolean;
23
+ /**
24
+ * The nesting level of an item. Applies an indent for columns with
25
+ * `Column.indent` set to `true`. `0`, negative values, and an unset value
26
+ * mean no indent.
19
27
  */
20
28
  level?: number;
29
+ /**
30
+ * When set to `true`, does not control item virtualization.
31
+ * Useful when you include `DefaultItemRenderer` as a part of a custom row renderer,
32
+ * and track the visibility yourself.
33
+ */
34
+ noItemVirtualization?: boolean;
35
+ /**
36
+ * When set to `true`, does not report the item's boundaries to the reorder system.
37
+ * Useful when you include `DefaultItemRenderer` as a part of a custom row renderer
38
+ * that registers boundaries itself.
39
+ */
40
+ noReorderLayout?: boolean;
21
41
  }
22
42
  /**
23
- * @see TableProps.renderItem
43
+ * Standard component for rendering a table row.
44
+ *
45
+ * Renders an item using the table's column definitions and lets you
46
+ * configure item-scoped behavior such as selection, keyboard navigation,
47
+ * event handlers, `className`, and `ref`.
48
+ *
49
+ * Note that row-level click and keyboard handlers are not discoverable
50
+ * by assistive technologies. Make sure that any functionality relying on
51
+ * them (such as selection or expand/collapse) is also available through
52
+ * accessible controls, such as checkboxes or buttons with accessible
53
+ * labels.
24
54
  */
25
- export declare function DefaultItemRenderer<T>({ ref: userRef, index, clickable, level, className, onKeyDown, onBlur, ...restProps }: DefaultItemRendererProps & ComponentPropsWithoutRef<'tr'>): import("react").JSX.Element;
55
+ export declare function DefaultItemRenderer<T>({ index, keyboardFocusable, clickable, selected, level, noItemVirtualization, noReorderLayout, ref: userRef, className, ...restProps }: DefaultItemRendererProps & ComponentPropsWithRef<'tr'>): import("react").JSX.Element | null;
@@ -1,61 +1,74 @@
1
- import { useContext, useEffect, useRef } from 'react';
1
+ import { use, useCallback, useRef } from 'react';
2
2
  import classNames from 'classnames';
3
- import { CollapseItemIntoSpacerContext, TablePropsContext } from './table-const';
4
- import { useIsIntersectingListener } from '../global/intersection-observer-context';
5
- import { TableCell, TableRow } from './table-base';
3
+ import { TablePropsContext } from './table-const';
4
+ import { useComposedRef } from '../global/compose-refs';
5
+ import { useItemVirtualization } from './item-virtualization';
6
+ import { TableCell, TableRow } from './table-primitives';
7
+ import { ReorderAnimationContext } from './internal/reorder-animation-context';
8
+ import { useReorderItemLayout } from './reorder-item-layout';
6
9
  import styles from './table.css';
7
- const INDENT_SIZE = 24;
8
10
  /**
9
- * @see TableProps.renderItem
11
+ * Standard component for rendering a table row.
12
+ *
13
+ * Renders an item using the table's column definitions and lets you
14
+ * configure item-scoped behavior such as selection, keyboard navigation,
15
+ * event handlers, `className`, and `ref`.
16
+ *
17
+ * Note that row-level click and keyboard handlers are not discoverable
18
+ * by assistive technologies. Make sure that any functionality relying on
19
+ * them (such as selection or expand/collapse) is also available through
20
+ * accessible controls, such as checkboxes or buttons with accessible
21
+ * labels.
10
22
  */
11
- export function DefaultItemRenderer({ ref: userRef, index, clickable, level, className, onKeyDown, onBlur, ...restProps }) {
12
- const selfRef = useRef(null);
13
- const ref = userRef ?? selfRef;
14
- const collapseItemIntoSpacer = useContext(CollapseItemIntoSpacerContext);
15
- useIsIntersectingListener(ref, isIntersecting => {
16
- if (ref.current && !isIntersecting) {
17
- collapseItemIntoSpacer(ref.current.getBoundingClientRect().height);
18
- }
23
+ export function DefaultItemRenderer({ index, keyboardFocusable, clickable, selected, level, noItemVirtualization, noReorderLayout, ref: userRef, className, ...restProps }) {
24
+ const localRef = useRef(null);
25
+ const composedRef = useComposedRef(userRef, localRef);
26
+ useItemVirtualization({
27
+ index,
28
+ refs: localRef,
29
+ onIntersectionChange: useCallback(([isIntersecting], _i, [element]) => isIntersecting === false &&
30
+ !noItemVirtualization &&
31
+ element?.isConnected &&
32
+ !element.contains(document.activeElement) &&
33
+ !element.previousElementSibling?.contains(document.activeElement) &&
34
+ !element.nextElementSibling?.contains(document.activeElement)
35
+ ? element.getBoundingClientRect().height
36
+ : undefined, [noItemVirtualization]),
19
37
  });
20
- const { data, columns, selection, isItemKeyboardFocusable, onItemFocus } = useContext(TablePropsContext);
21
- const item = data[index];
22
- const selected = selection?.isSelected(item);
23
- function handleKeyDown(e) {
24
- onKeyDown?.(e);
25
- if (!e.defaultPrevented && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
26
- const step = e.key === 'ArrowUp' ? -1 : 1;
27
- // eslint-disable-next-line yoda
28
- for (let i = index + step; 0 <= i && i < data.length; i += step) {
29
- if (isItemKeyboardFocusable?.(data[i], i, data)) {
30
- onItemFocus?.(data[i], i, data);
31
- break;
32
- }
33
- }
34
- }
35
- }
36
- const focused = selection?.isFocused(item);
37
- useEffect(() => {
38
- if (focused)
39
- ref.current?.focus();
40
- }, [focused, ref]);
41
- function handleBlur(e) {
42
- onBlur?.(e);
43
- if (!e.defaultPrevented && focused) {
44
- onItemFocus?.(null, -1, data);
45
- }
38
+ useReorderItemLayout({
39
+ disabled: noReorderLayout,
40
+ index,
41
+ getBounds: () => {
42
+ const r = localRef.current?.getBoundingClientRect();
43
+ return { start: r?.top ?? 0, end: r?.bottom ?? 0 };
44
+ },
45
+ });
46
+ const tableProps = use(TablePropsContext);
47
+ if (!tableProps) {
48
+ return null;
46
49
  }
47
- return (<TableRow ref={ref} className={classNames(className, clickable && styles.clickableRow, selected && styles.selectedRow)} onKeyDown={handleKeyDown} tabIndex={focused ? 0 : undefined} onBlur={focused ? handleBlur : undefined} {...restProps}>
48
- {columns.map((column, columnIndex) => (<TableCell key={column.key} className={column.tdClassName?.(item, index, data)} style={column.indent && level != null && level > 0 ? { paddingInlineStart: `${level * INDENT_SIZE}px` } : undefined}>
49
- {column.renderCell?.(item, index, data) ?? getDefaultCellValue(item, columnIndex)}
50
- </TableCell>))}
50
+ const { reorderAnimation } = use(ReorderAnimationContext);
51
+ const isAnimatedItem = reorderAnimation?.direction === 'items' && reorderAnimation.index === index;
52
+ const animatedColumnIndex = reorderAnimation?.direction === 'columns' ? reorderAnimation.index : undefined;
53
+ const animateClassName = reorderAnimation?.className;
54
+ const { data, columns } = tableProps;
55
+ const item = data[index];
56
+ const indentSize = 24;
57
+ return (<TableRow ref={composedRef} keyboardFocusable={keyboardFocusable} className={classNames(className, clickable && styles.clickableRow, selected && styles.selectedRow, isAnimatedItem && animateClassName)} {...restProps}>
58
+ {columns.map((column, columnIndex) => {
59
+ const { key, tdClassName, indent } = column;
60
+ return (<TableCell key={key} className={classNames(columnIndex === animatedColumnIndex && animateClassName, typeof tdClassName === 'function' ? tdClassName(item, index, data) : tdClassName)} style={indent && level != null && level > 0 ? { paddingInlineStart: `${level * indentSize}px` } : undefined}>
61
+ {column.renderCell?.(item, index, data) ?? getDefaultCellValue(item, columnIndex, key)}
62
+ </TableCell>);
63
+ })}
51
64
  </TableRow>);
52
65
  }
53
- function getDefaultCellValue(item, columnIndex) {
66
+ function getDefaultCellValue(item, columnIndex, columnKey) {
54
67
  if (Array.isArray(item)) {
55
- return String(item[columnIndex] ?? '');
68
+ return String(item[columnIndex]);
56
69
  }
57
70
  if (item !== null && typeof item === 'object') {
58
- return String(Object.values(item)[columnIndex] ?? '');
71
+ return String(item[String(columnKey)]);
59
72
  }
60
73
  if (columnIndex === 0) {
61
74
  return String(item);
@@ -0,0 +1,21 @@
1
+ import { type RefObject } from 'react';
2
+ import type { Column } from '../table-props';
3
+ import type { ReorderAnimation } from '../reorder-animation';
4
+ export interface ReorderSpec {
5
+ direction: 'columns' | 'items';
6
+ fromIndex: number;
7
+ insertionIndex: number;
8
+ }
9
+ interface ReorderAnimationContextValue {
10
+ reorderAnimation: ReorderAnimation | null;
11
+ expectReorder: (reorderSpec: ReorderSpec) => void;
12
+ }
13
+ export declare const ReorderAnimationContext: import("react").Context<ReorderAnimationContextValue>;
14
+ export declare function useReorderAnimationContextValue<T>({ noColumnReorderAnimation, noItemReorderAnimation, tableRef, data, columns, }: {
15
+ noColumnReorderAnimation: boolean | undefined;
16
+ noItemReorderAnimation: boolean | undefined;
17
+ tableRef: RefObject<HTMLTableElement | null>;
18
+ data: readonly T[];
19
+ columns: readonly Column<T>[];
20
+ }): ReorderAnimationContextValue;
21
+ export {};
@@ -0,0 +1,60 @@
1
+ import { createContext, useEffect, useRef, useState } from 'react';
2
+ import { parseCssDuration } from '../../global/parse-css-duration';
3
+ import { requestAnimationFrameWithCleanup, setTimeoutWithCleanup } from '../../global/schedule-with-cleanup';
4
+ import styles from '../table.css';
5
+ /** How long to wait for the data to change after {@link expectReorder} is called. */
6
+ const pendingReorderTimeoutMs = 1000;
7
+ export const ReorderAnimationContext = createContext({
8
+ reorderAnimation: null,
9
+ expectReorder: () => { },
10
+ });
11
+ export function useReorderAnimationContextValue({ noColumnReorderAnimation, noItemReorderAnimation, tableRef, data, columns, }) {
12
+ const [reorderAnimation, setReorderAnimation] = useState(null);
13
+ const pendingReorderRef = useRef(null);
14
+ function expectReorder(reorderSpec) {
15
+ const isColumn = reorderSpec.direction === 'columns';
16
+ if ((isColumn && noColumnReorderAnimation) || (!isColumn && noItemReorderAnimation))
17
+ return;
18
+ if (pendingReorderRef.current) {
19
+ window.clearTimeout(pendingReorderRef.current.timerId);
20
+ }
21
+ const timerId = window.setTimeout(() => {
22
+ pendingReorderRef.current = null;
23
+ }, pendingReorderTimeoutMs);
24
+ pendingReorderRef.current = { ...reorderSpec, timerId, columns, data };
25
+ }
26
+ useEffect(() => {
27
+ return () => {
28
+ if (pendingReorderRef.current) {
29
+ window.clearTimeout(pendingReorderRef.current.timerId);
30
+ pendingReorderRef.current = null;
31
+ }
32
+ };
33
+ }, []);
34
+ useEffect(() => {
35
+ const table = tableRef.current;
36
+ const pendingReorder = pendingReorderRef.current;
37
+ if (!table || !pendingReorder)
38
+ return;
39
+ const { direction, data: pendingData, columns: pendingColumns } = pendingReorder;
40
+ const isColumn = direction === 'columns';
41
+ if ((isColumn && columns === pendingColumns) || (!isColumn && data === pendingData))
42
+ return;
43
+ pendingReorderRef.current = null;
44
+ const { fromIndex, insertionIndex } = pendingReorder;
45
+ // Moving forward shifts the array by one, hence -1
46
+ const index = fromIndex < insertionIndex ? insertionIndex - 1 : insertionIndex;
47
+ return requestAnimationFrameWithCleanup(() => setReorderAnimation(prev => prev == null ? { direction, index, phase: 'initial', className: styles.reorderAnimationInitial } : prev));
48
+ }, [data, columns, tableRef]);
49
+ useEffect(() => {
50
+ if (reorderAnimation?.phase === 'initial') {
51
+ return requestAnimationFrameWithCleanup(() => setReorderAnimation(prev => prev === reorderAnimation ? { ...prev, phase: 'fade-out', className: styles.reorderAnimationFadeOut } : prev));
52
+ }
53
+ if (reorderAnimation?.phase === 'fade-out') {
54
+ const fadeOutMs = parseCssDuration(window.getComputedStyle(tableRef.current).getPropertyValue('--reorder-animation-fade-out-duration'));
55
+ return setTimeoutWithCleanup(() => setReorderAnimation(prev => (prev === reorderAnimation ? null : prev)), fadeOutMs);
56
+ }
57
+ return undefined;
58
+ }, [reorderAnimation, tableRef]);
59
+ return { reorderAnimation, expectReorder };
60
+ }
@@ -0,0 +1,9 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import type { DragState } from '../table-primitives';
3
+ export declare function ReorderHandle<T>({ direction, index, noDragFrame, noHandleTranslate, onUserDrag, ref: userRef, className, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onLostPointerCapture, ...restProps }: {
4
+ direction: 'columns' | 'items';
5
+ index: number;
6
+ noDragFrame?: boolean;
7
+ noHandleTranslate?: boolean;
8
+ onUserDrag?: (state: DragState) => void;
9
+ } & ComponentPropsWithRef<'button'>): import("react").JSX.Element;