@react-fabric/core 1.0.0-beta.5 → 1.0.0-beta.7

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 (70) hide show
  1. package/dist/cjs/components/animations/Animations.js +12 -5
  2. package/dist/cjs/components/avatar/Avatar.js +6 -7
  3. package/dist/cjs/components/button/ConfirmButton.js +4 -2
  4. package/dist/cjs/components/card/Card.js +4 -2
  5. package/dist/cjs/components/collapsable/Collapsable.js +3 -2
  6. package/dist/cjs/components/dropdown/Dropdown.js +2 -1
  7. package/dist/cjs/components/dropdown/DropdownTool.js +3 -2
  8. package/dist/cjs/components/icon/Icon.js +7 -7
  9. package/dist/cjs/components/menu/ContextMenu.js +30 -10
  10. package/dist/cjs/components/menu/Menu.js +1 -1
  11. package/dist/cjs/components/navigator/Navigator.js +2 -0
  12. package/dist/cjs/components/panel/Panel.js +2 -0
  13. package/dist/cjs/components/tabpanel/Tab.js +54 -36
  14. package/dist/cjs/core/aside/Aside.js +3 -0
  15. package/dist/cjs/core/content/Content.js +8 -5
  16. package/dist/cjs/core/page/Page.js +1 -0
  17. package/dist/cjs/types/icons.js +1 -0
  18. package/dist/cjs/typography/Typography.module.css +1 -1
  19. package/dist/esm/components/animations/Animations.js +12 -5
  20. package/dist/esm/components/avatar/Avatar.js +7 -8
  21. package/dist/esm/components/button/ConfirmButton.js +4 -2
  22. package/dist/esm/components/card/Card.js +4 -2
  23. package/dist/esm/components/collapsable/Collapsable.js +3 -2
  24. package/dist/esm/components/dropdown/Dropdown.js +2 -1
  25. package/dist/esm/components/dropdown/DropdownTool.js +3 -2
  26. package/dist/esm/components/icon/Icon.js +7 -7
  27. package/dist/esm/components/menu/ContextMenu.js +32 -12
  28. package/dist/esm/components/menu/Menu.js +1 -1
  29. package/dist/esm/components/navigator/Navigator.js +2 -0
  30. package/dist/esm/components/panel/Panel.js +2 -0
  31. package/dist/esm/components/tabpanel/Tab.js +55 -37
  32. package/dist/esm/core/aside/Aside.js +3 -0
  33. package/dist/esm/core/content/Content.js +8 -5
  34. package/dist/esm/core/page/Page.js +1 -0
  35. package/dist/esm/types/icons.js +1 -0
  36. package/dist/esm/typography/Typography.module.css +1 -1
  37. package/dist/types/components/actionLabel/ActionLabel.d.ts +2 -2
  38. package/dist/types/components/animations/Animations.d.ts +1 -1
  39. package/dist/types/components/button/ConfirmButton.d.ts +6 -1
  40. package/dist/types/components/card/Card.d.ts +3 -3
  41. package/dist/types/components/collapsable/Collapsable.d.ts +5 -1
  42. package/dist/types/components/dropdown/DropdownTool.d.ts +2 -1
  43. package/dist/types/components/media/Media.d.ts +2 -2
  44. package/dist/types/components/menu/ContextMenu.d.ts +2 -1
  45. package/dist/types/components/tabpanel/Tab.d.ts +4 -4
  46. package/dist/types/core/content/Content.d.ts +3 -2
  47. package/dist/types/types/icons.d.ts +1 -0
  48. package/dist/types/types/index.d.ts +4 -0
  49. package/package.json +3 -3
  50. package/src/components/actionLabel/ActionLabel.tsx +12 -3
  51. package/src/components/animations/Animations.tsx +8 -2
  52. package/src/components/avatar/Avatar.tsx +68 -81
  53. package/src/components/button/ConfirmButton.tsx +10 -4
  54. package/src/components/card/Card.tsx +4 -0
  55. package/src/components/collapsable/Collapsable.tsx +6 -0
  56. package/src/components/dropdown/Dropdown.tsx +1 -0
  57. package/src/components/dropdown/DropdownTool.tsx +3 -1
  58. package/src/components/icon/Icon.tsx +45 -57
  59. package/src/components/media/Media.tsx +2 -2
  60. package/src/components/menu/ContextMenu.tsx +36 -11
  61. package/src/components/menu/Menu.tsx +1 -1
  62. package/src/components/navigator/Navigator.tsx +2 -0
  63. package/src/components/panel/Panel.tsx +2 -0
  64. package/src/components/tabpanel/Tab.tsx +73 -59
  65. package/src/core/aside/Aside.tsx +3 -0
  66. package/src/core/content/Content.tsx +16 -10
  67. package/src/core/page/Page.tsx +1 -0
  68. package/src/types/icons.ts +1 -0
  69. package/src/types/index.ts +5 -0
  70. package/src/typography/Typography.module.css +1 -1
@@ -29,30 +29,41 @@ import {
29
29
  useClick,
30
30
  useDismiss,
31
31
  useFloating,
32
+ useFloatingNodeId,
32
33
  useInteractions,
33
34
  } from "@floating-ui/react";
34
- import { cloneElement, useRef, useState } from "react";
35
+ import { cloneElement, useEffect, useRef, useState } from "react";
35
36
  import { type ChildrenProp } from "../../types";
36
37
  import { type Menu } from "./Menu";
37
38
 
38
39
  export interface ContextMenuProps extends ChildrenProp {
39
40
  menu: React.ReactElement<typeof Menu>;
41
+
42
+ disabled?: boolean;
40
43
  }
41
44
 
42
- export const ContextMenu = ({ children, menu }: ContextMenuProps) => {
45
+ export const ContextMenu = ({ children, menu, disabled }: ContextMenuProps) => {
43
46
  const [isOpen, setIsOpen] = useState(false);
47
+ const nodeId = useFloatingNodeId();
44
48
 
45
49
  const { refs, floatingStyles, context } = useFloating({
46
50
  open: isOpen,
47
- onOpenChange: setIsOpen,
51
+ nodeId,
52
+ onOpenChange: (open, evt, reason) => {
53
+ const target = evt?.target as HTMLElement;
54
+ if (!target?.closest(".menu-list")) setIsOpen(open);
55
+ },
48
56
  strategy: "fixed",
49
57
  placement: "bottom-start",
50
58
  whileElementsMounted: autoUpdate,
51
59
  middleware: [offset(8), flip()],
52
60
  });
53
61
 
54
- const click = useClick(context, {});
55
- const dismiss = useDismiss(context, {});
62
+ const click = useClick(context, {
63
+ toggle: true,
64
+ ignoreMouse: true,
65
+ });
66
+ const dismiss = useDismiss(context, { bubbles: true });
56
67
 
57
68
  const { getFloatingProps } = useInteractions([dismiss, click]);
58
69
 
@@ -78,10 +89,19 @@ export const ContextMenu = ({ children, menu }: ContextMenuProps) => {
78
89
  return false;
79
90
  });
80
91
 
81
- const wrapperRef = useRef((el: HTMLElement) => {
82
- el?.parentElement?.removeEventListener("contextmenu", handleClick.current);
83
- el?.parentElement?.addEventListener("contextmenu", handleClick.current);
84
- });
92
+ const wrapperRef = useRef<HTMLDivElement>(null);
93
+ useEffect(() => {
94
+ const el = wrapperRef.current;
95
+ if (el && !disabled) {
96
+ el?.parentElement?.addEventListener("contextmenu", handleClick.current);
97
+ return () => {
98
+ el?.parentElement?.removeEventListener(
99
+ "contextmenu",
100
+ handleClick.current,
101
+ );
102
+ };
103
+ }
104
+ }, [disabled]);
85
105
 
86
106
  const tryClosing = useRef((e: React.MouseEvent) => {
87
107
  if ((e.target as HTMLElement)?.closest("[data-dropdown-dismiss='true']"))
@@ -91,12 +111,17 @@ export const ContextMenu = ({ children, menu }: ContextMenuProps) => {
91
111
  return (
92
112
  <div
93
113
  className="contents"
94
- ref={wrapperRef.current as AnyObject}
114
+ ref={wrapperRef}
95
115
  onMouseUpCapture={tryClosing.current}
96
116
  >
97
117
  {children}
98
118
  {isOpen && (
99
- <FloatingPortal>
119
+ <FloatingPortal
120
+ root={
121
+ refs.domReference.current?.closest<HTMLElement>(".theme-base") ??
122
+ undefined
123
+ }
124
+ >
100
125
  {cloneElement(menu as AnyObject, {
101
126
  ...getFloatingProps(),
102
127
  style: {
@@ -224,7 +224,7 @@ const MenuComponent = ({
224
224
  "outline shadow-lg rounded-capped max-h-96 overflow-auto scroll-thin z-[var(--z-popover)]",
225
225
  )}
226
226
  autoFocus
227
- ref={mergeRefs(refs.setFloating, isNested ? undefined : ref)}
227
+ ref={mergeRefs(isNested ? refs.setFloating : ref)}
228
228
  {...getFloatingProps({
229
229
  onClick: isNested ? undefined : handleClick,
230
230
  })}
@@ -61,6 +61,7 @@ export const Navigator = ({
61
61
  color={color}
62
62
  className="text-[1em] p-1"
63
63
  aria-label="previous"
64
+ data-testid="previous"
64
65
  icon={CoreIcons.chevronLeft}
65
66
  onClick={(e) => [e.stopPropagation(), onNavigate(-1)]}
66
67
  />
@@ -69,6 +70,7 @@ export const Navigator = ({
69
70
  className="text-[1em] p-1"
70
71
  color={color}
71
72
  aria-label="next"
73
+ data-testid="next"
72
74
  icon={CoreIcons.chevronRight}
73
75
  onClick={(e) => [e.stopPropagation(), onNavigate(1)]}
74
76
  />
@@ -176,6 +176,7 @@ export const Panel = ({
176
176
  headerClassName,
177
177
  "flex flex-nowrap items-center area-head",
178
178
  )}
179
+ data-testid="panel-header"
179
180
  onClick={collapsable ? toggleCollapse : undefined}
180
181
  >
181
182
  {onBack && (
@@ -240,6 +241,7 @@ export const Panel = ({
240
241
  </header>
241
242
  {(!collapsed || expanded) && (
242
243
  <div
244
+ data-testid="panel-body"
243
245
  className={classNames(
244
246
  classes.panelBody,
245
247
  "area-content grid overflow-hidden",
@@ -21,8 +21,10 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
+ import { isString } from "@react-fabric/utilities";
24
25
  import classNames from "classnames";
25
- import { type ReactElement, useMemo } from "react";
26
+ import { Fragment, type ReactElement, useMemo } from "react";
27
+ import { Tooltip } from "../../overlays";
26
28
  import {
27
29
  type AriaProps,
28
30
  type BadgeType,
@@ -34,6 +36,7 @@ import {
34
36
  type PolymorphicProps,
35
37
  type RefProp,
36
38
  type TestProps,
39
+ type TooltipProp,
37
40
  } from "../../types";
38
41
  import { Badge, getBadgeProps } from "../badge/Badge";
39
42
  import { DropdownTool } from "../dropdown/DropdownTool";
@@ -46,7 +49,8 @@ export interface TabProps
46
49
  RefProp,
47
50
  AriaProps,
48
51
  TestProps,
49
- IconProps {
52
+ IconProps,
53
+ TooltipProp {
50
54
  /**
51
55
  * tab id
52
56
  */
@@ -62,7 +66,7 @@ export interface TabProps
62
66
  /**
63
67
  * tab badge
64
68
  */
65
- badge?: BadgeType;
69
+ badge?: string | number | BadgeType;
66
70
  /**
67
71
  * disabled
68
72
  */
@@ -110,6 +114,7 @@ export const Tab = <Tag extends React.ElementType = "button">({
110
114
  tabFlex,
111
115
  color,
112
116
  actions,
117
+ tooltip,
113
118
  activeClassName,
114
119
  ...aria
115
120
  }: TabProps & PolymorphicProps<Tag>) => {
@@ -117,63 +122,72 @@ export const Tab = <Tag extends React.ElementType = "button">({
117
122
  return getBadgeProps(badge);
118
123
  }, [badge]);
119
124
 
125
+ const tooltipProps = useMemo(() => {
126
+ if (isString(tooltip)) return { content: tooltip };
127
+
128
+ return tooltip ?? {};
129
+ }, [tooltip]);
130
+
120
131
  const E = as ?? "button";
132
+ const W = tooltip ? Tooltip : Fragment;
121
133
  return (
122
- <E
123
- data-id={id}
124
- disabled={disabled}
125
- data-color={color}
126
- className={classNames(
127
- classes.tabButton,
128
- className,
129
- active && "active pointer-events-none",
130
- active && activeClassName,
131
- disabled && "text-tint-500 pointer-events-none",
132
- tabFlex ? "flex-1" : "flex-initial max-w-36 min-w-24",
133
- "flex items-center justify-center px-1 rounded overflow-hidden",
134
- )}
135
- onClick={() => !active && onClick?.(id)}
136
- ref={ref as AnyObject}
137
- {...aria}
138
- >
139
- {icon && (
140
- <Icon
141
- icon={icon}
142
- bg={iconBg}
143
- color={iconColor}
144
- rtlFlip={rtlFlip}
145
- className="flex-content"
146
- />
147
- )}
148
- {!minimal && (
149
- <label className="flex-initial truncate text-center px-1 py-1">
150
- {label}
151
- </label>
152
- )}
153
- {badge && (
154
- <Badge
155
- {...badgeProps}
156
- placement={minimal ? "end" : ""}
157
- className={classNames("me-1", badgeProps.className)}
158
- />
159
- )}
160
- {actions && active && (
161
- <DropdownTool className="bg-base/20 me-1 z-5">{actions}</DropdownTool>
162
- )}
163
- {onClose && (
164
- <span
165
- role="none"
166
- className={classNames(
167
- "cursor-pointer me-1 opacity-65 hover:opacity-90 pointer-events-auto",
168
- )}
169
- onClick={(e) => {
170
- onClose?.();
171
- e.stopPropagation();
172
- }}
173
- >
174
- &times;
175
- </span>
176
- )}
177
- </E>
134
+ <W {...tooltipProps}>
135
+ <E
136
+ data-id={id}
137
+ disabled={disabled}
138
+ data-color={color}
139
+ className={classNames(
140
+ classes.tabButton,
141
+ className,
142
+ active && "active pointer-events-none",
143
+ active && activeClassName,
144
+ disabled && "text-tint-500 pointer-events-none",
145
+ tabFlex ? "flex-1" : "flex-initial max-w-36 min-w-24",
146
+ "flex items-center justify-center px-1 rounded overflow-hidden",
147
+ )}
148
+ onClick={() => !active && onClick?.(id)}
149
+ ref={ref as AnyObject}
150
+ {...aria}
151
+ >
152
+ {icon && (
153
+ <Icon
154
+ icon={icon}
155
+ bg={iconBg}
156
+ color={iconColor}
157
+ rtlFlip={rtlFlip}
158
+ className="flex-content"
159
+ />
160
+ )}
161
+ {!minimal && (
162
+ <label className="flex-initial truncate text-center px-1 py-1">
163
+ {label}
164
+ </label>
165
+ )}
166
+ {badge && (
167
+ <Badge
168
+ {...badgeProps}
169
+ placement={minimal ? "end" : ""}
170
+ className={classNames("me-1", badgeProps.className)}
171
+ />
172
+ )}
173
+ {actions && active && (
174
+ <DropdownTool className="bg-base/20 me-1 z-5">{actions}</DropdownTool>
175
+ )}
176
+ {onClose && (
177
+ <span
178
+ role="none"
179
+ className={classNames(
180
+ "cursor-pointer me-1 opacity-65 hover:opacity-90 pointer-events-auto",
181
+ )}
182
+ onClick={(e) => {
183
+ onClose?.();
184
+ e.stopPropagation();
185
+ }}
186
+ >
187
+ &times;
188
+ </span>
189
+ )}
190
+ </E>
191
+ </W>
178
192
  );
179
193
  };
@@ -200,6 +200,7 @@ export const Aside = ({
200
200
  className={classNames(classes.aside, className, "overflow-hidden grid")}
201
201
  data-align={align}
202
202
  data-flyout={peek}
203
+ data-loading={loading}
203
204
  data-collapsed={isCollapsed}
204
205
  style={isCollapsed ? undefined : widthStyles}
205
206
  data-testid={testId}
@@ -211,6 +212,7 @@ export const Aside = ({
211
212
  headerClassName,
212
213
  "grid area-head items-center",
213
214
  )}
215
+ data-testid="aside-header"
214
216
  >
215
217
  {title && (
216
218
  <div
@@ -264,6 +266,7 @@ export const Aside = ({
264
266
  "overflow-hidden grid",
265
267
  )}
266
268
  style={isCollapsed ? widthStyles : undefined}
269
+ data-testid="aside-body"
267
270
  >
268
271
  <ErrorBoundary>{children}</ErrorBoundary>
269
272
  </section>
@@ -22,14 +22,14 @@
22
22
  */
23
23
 
24
24
  import classNames from "classnames";
25
- import { useImperativeHandle } from "react";
25
+ import { type MouseEvent, useImperativeHandle } from "react";
26
26
  import { useDebounce } from "../../hooks/useDebounce";
27
27
  import { useResizeObserver } from "../../hooks/useResizeObserver";
28
28
  import {
29
29
  type ChildrenProp,
30
30
  type CssProp,
31
- type PositionObject,
32
31
  type RefProp,
32
+ type ScrollObject,
33
33
  type SizeObject,
34
34
  type TestProps,
35
35
  } from "../../types";
@@ -44,7 +44,7 @@ export interface ContentProps
44
44
  /**
45
45
  * scroll vevent handler
46
46
  */
47
- onScroll?: (event: PositionObject) => void;
47
+ onScroll?: (scroll: ScrollObject, event: MouseEvent<HTMLDivElement>) => void;
48
48
  /**
49
49
  * scroll vevent handler
50
50
  */
@@ -65,13 +65,19 @@ export const Content = ({
65
65
  }: ContentProps) => {
66
66
  const resizeHandle = useResizeObserver(onResize);
67
67
  const scrollHandler = useDebounce(
68
- (e: React.UIEvent<HTMLDivElement>) => {
69
- onScroll?.({
70
- // @ts-expect-error ignore
71
- left: e.target.scrollLeft,
72
- // @ts-expect-error ignore
73
- top: e.target.scrollTop,
74
- });
68
+ (e: MouseEvent<HTMLDivElement>) => {
69
+ const el = e.target as HTMLElement;
70
+ onScroll?.(
71
+ {
72
+ left: el.scrollLeft,
73
+ top: el.scrollTop,
74
+ scrollHeight: el.scrollHeight,
75
+ scrollWidth: el.scrollWidth,
76
+ width: el.offsetWidth,
77
+ height: el.offsetHeight,
78
+ },
79
+ e,
80
+ );
75
81
  },
76
82
  [onScroll],
77
83
  );
@@ -66,6 +66,7 @@ export const Page = ({
66
66
  <div
67
67
  data-testid={testId}
68
68
  data-test-value={testValue}
69
+ data-loading={loading}
69
70
  className={classNames(
70
71
  classes.page,
71
72
  "overflow-hidden grid area-content",
@@ -249,6 +249,7 @@ export module CoreIcons {
249
249
  "M5.541 2.159c-0.153-0.1-0.34-0.159-0.541-0.159-0.552 0-1 0.448-1 1v18c-0.001 0.182 0.050 0.372 0.159 0.541 0.299 0.465 0.917 0.599 1.382 0.3l14-9c0.114-0.072 0.219-0.174 0.3-0.3 0.299-0.465 0.164-1.083-0.3-1.382zM6 4.832l11.151 7.168-11.151 7.168z";
250
250
  export const pause =
251
251
  "M6 3c-0.552 0-1 0.448-1 1v16c0 0.552 0.448 1 1 1h4c0.552 0 1-0.448 1-1v-16c0-0.552-0.448-1-1-1zM7 5h2v14h-2zM14 3c-0.552 0-1 0.448-1 1v16c0 0.552 0.448 1 1 1h4c0.552 0 1-0.448 1-1v-16c0-0.552-0.448-1-1-1zM15 5h2v14h-2z";
252
+ export const stop = "M18,18H6V6H18V18Z";
252
253
  export const volumeOn =
253
254
  "M10 7.081v9.839l-3.375-2.7c-0.17-0.137-0.388-0.22-0.625-0.22h-3v-4h3c0.218 0.001 0.439-0.071 0.625-0.219zM10.375 4.219l-4.726 3.781h-3.649c-0.552 0-1 0.448-1 1v6c0 0.552 0.448 1 1 1h3.649l4.726 3.781c0.431 0.345 1.061 0.275 1.406-0.156 0.148-0.185 0.22-0.407 0.219-0.625v-14c0-0.552-0.448-1-1-1-0.237 0-0.455 0.083-0.625 0.219zM18.363 5.637c1.757 1.758 2.635 4.059 2.635 6.364 0 2.304-0.878 4.605-2.635 6.362-0.39 0.391-0.39 1.024 0 1.414s1.024 0.39 1.414 0c2.147-2.147 3.22-4.963 3.221-7.776s-1.074-5.63-3.221-7.778c-0.39-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414zM14.833 9.167c0.781 0.781 1.171 1.803 1.171 2.828s-0.39 2.047-1.171 2.828c-0.39 0.391-0.39 1.024 0 1.414s1.024 0.39 1.414 0c1.171-1.171 1.757-2.708 1.757-4.242s-0.586-3.071-1.757-4.242c-0.39-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z";
254
255
  export const volumeOff =
@@ -239,3 +239,8 @@ export interface SizeObject {
239
239
  width: number;
240
240
  height: number;
241
241
  }
242
+
243
+ export interface ScrollObject extends PositionObject, SizeObject {
244
+ scrollWidth?: number;
245
+ scrollHeight: number;
246
+ }
@@ -57,7 +57,7 @@
57
57
  }
58
58
 
59
59
  .mark {
60
- --mark-color: var(--accent);
60
+ --mark-color: var(--primary);
61
61
 
62
62
  background-color: color-mix(
63
63
  in lab,