@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
@@ -1,5 +1,5 @@
1
- import { type AriaProps, type CssProp } from "../../types";
2
- export interface BaseProps extends CssProp, AriaProps {
1
+ import { type AriaProps, type CssProp, type TestProps } from "../../types";
2
+ export interface BaseProps extends CssProp, TestProps, AriaProps {
3
3
  /**
4
4
  * media source
5
5
  */
@@ -3,5 +3,6 @@ import { type ChildrenProp } from "../../types";
3
3
  import { type Menu } from "./Menu";
4
4
  export interface ContextMenuProps extends ChildrenProp {
5
5
  menu: React.ReactElement<typeof Menu>;
6
+ disabled?: boolean;
6
7
  }
7
- export declare const ContextMenu: ({ children, menu }: ContextMenuProps) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const ContextMenu: ({ children, menu, disabled }: ContextMenuProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { type ReactElement } from "react";
2
- import { type AriaProps, type BadgeType, type CallbackReturn, type ChildrenProp, type ColorType, type CssProp, type IconProps, type PolymorphicProps, type RefProp, type TestProps } from "../../types";
3
- export interface TabProps extends Partial<ChildrenProp>, CssProp, RefProp, AriaProps, TestProps, IconProps {
2
+ import { type AriaProps, type BadgeType, type CallbackReturn, type ChildrenProp, type ColorType, type CssProp, type IconProps, type PolymorphicProps, type RefProp, type TestProps, type TooltipProp } from "../../types";
3
+ export interface TabProps extends Partial<ChildrenProp>, CssProp, RefProp, AriaProps, TestProps, IconProps, TooltipProp {
4
4
  /**
5
5
  * tab id
6
6
  */
@@ -16,7 +16,7 @@ export interface TabProps extends Partial<ChildrenProp>, CssProp, RefProp, AriaP
16
16
  /**
17
17
  * tab badge
18
18
  */
19
- badge?: BadgeType;
19
+ badge?: string | number | BadgeType;
20
20
  /**
21
21
  * disabled
22
22
  */
@@ -42,4 +42,4 @@ export interface TabProps extends Partial<ChildrenProp>, CssProp, RefProp, AriaP
42
42
  */
43
43
  onClick?: (id: string) => CallbackReturn;
44
44
  }
45
- export declare const Tab: <Tag extends import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> = "button">({ ref, as, id, className, label, icon, iconBg, iconColor, rtlFlip, badge, disabled, onClose, active, minimal, onClick, tabFlex, color, actions, activeClassName, ...aria }: TabProps & PolymorphicProps<Tag>) => import("react/jsx-runtime").JSX.Element;
45
+ export declare const Tab: <Tag extends import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> = "button">({ ref, as, id, className, label, icon, iconBg, iconColor, rtlFlip, badge, disabled, onClose, active, minimal, onClick, tabFlex, color, actions, tooltip, activeClassName, ...aria }: TabProps & PolymorphicProps<Tag>) => import("react/jsx-runtime").JSX.Element;
@@ -1,9 +1,10 @@
1
- import { type ChildrenProp, type CssProp, type PositionObject, type RefProp, type SizeObject, type TestProps } from "../../types";
1
+ import { type MouseEvent } from "react";
2
+ import { type ChildrenProp, type CssProp, type RefProp, type ScrollObject, type SizeObject, type TestProps } from "../../types";
2
3
  export interface ContentProps extends ChildrenProp, CssProp, RefProp, TestProps {
3
4
  /**
4
5
  * scroll vevent handler
5
6
  */
6
- onScroll?: (event: PositionObject) => void;
7
+ onScroll?: (scroll: ScrollObject, event: MouseEvent<HTMLDivElement>) => void;
7
8
  /**
8
9
  * scroll vevent handler
9
10
  */
@@ -105,6 +105,7 @@ export declare namespace CoreIcons {
105
105
  const mic = "M12 2c0.553 0 1.051 0.223 1.414 0.586s0.586 0.861 0.586 1.414v8c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414v-8c0-0.553 0.223-1.051 0.586-1.414s0.861-0.586 1.414-0.586zM12 0c-1.104 0-2.106 0.449-2.828 1.172s-1.172 1.724-1.172 2.828v8c0 1.104 0.449 2.106 1.172 2.828s1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828v-8c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172zM8 24h8c0.552 0 1-0.448 1-1s-0.448-1-1-1h-3v-2.062c1.809-0.226 3.432-1.056 4.657-2.281 1.446-1.447 2.343-3.448 2.343-5.657v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1v2c0 1.657-0.67 3.156-1.757 4.243s-2.586 1.757-4.243 1.757-3.156-0.67-4.243-1.757-1.757-2.586-1.757-4.243v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1v2c0 2.209 0.897 4.21 2.343 5.657 1.225 1.225 2.847 2.055 4.657 2.281v2.062h-3c-0.552 0-1 0.448-1 1s0.448 1 1 1z";
106
106
  const play = "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";
107
107
  const pause = "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";
108
+ const stop = "M18,18H6V6H18V18Z";
108
109
  const volumeOn = "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";
109
110
  const volumeOff = "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.219zM16.293 9.707l2.293 2.293-2.293 2.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0l2.293-2.293 2.293 2.293c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-2.293-2.293 2.293-2.293c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-2.293 2.293-2.293-2.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z";
110
111
  const alignLeft = "M3,3H21V5H3V3M3,7H15V9H3V7M3,11H21V13H3V11M3,15H15V17H3V15M3,19H21V21H3V19Z";
@@ -138,3 +138,7 @@ export interface SizeObject {
138
138
  width: number;
139
139
  height: number;
140
140
  }
141
+ export interface ScrollObject extends PositionObject, SizeObject {
142
+ scrollWidth?: number;
143
+ scrollHeight: number;
144
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/core",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.7",
4
4
  "description": "RUF: Core package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@floating-ui/react": "^0.26.24",
39
- "@react-fabric/utilities": "^1.0.0-beta.5",
39
+ "@react-fabric/utilities": "^1.0.0-beta.7",
40
40
  "@tailwindcss/container-queries": "^0.1.1",
41
41
  "@tailwindcss/forms": "^0.5.7",
42
42
  "@tailwindcss/typography": "^0.5.13",
@@ -56,5 +56,5 @@
56
56
  "node": ">=18.12.0",
57
57
  "npm": ">=8.19.0"
58
58
  },
59
- "gitHead": "7d4a7ed1ea9890652826bef58d01c31280d761c8"
59
+ "gitHead": "8e6efedbbb02660c0b64140aff00e633b891bb85"
60
60
  }
@@ -22,12 +22,21 @@
22
22
  */
23
23
 
24
24
  import classNames from "classnames";
25
- import { type ReactElement, useCallback } from "react";
26
- import { type AriaProps, type ChildProp, type CssProp } from "../../types";
25
+ import { useCallback, type ReactElement } from "react";
26
+ import {
27
+ type AriaProps,
28
+ type ChildProp,
29
+ type CssProp,
30
+ type TestProps,
31
+ } from "../../types";
27
32
  import { ButtonGroup } from "../button/ButtonGroup";
28
33
  import classes from "./ActionLabel.module.css";
29
34
 
30
- export interface ActionLabelProps extends AriaProps, CssProp, ChildProp {
35
+ export interface ActionLabelProps
36
+ extends AriaProps,
37
+ CssProp,
38
+ ChildProp,
39
+ TestProps {
31
40
  /**
32
41
  * action buttons aligment
33
42
  */
@@ -142,6 +142,7 @@ export const AnimationSpinner = ({
142
142
  className={classNames(
143
143
  "absolute inset-0 z-10 flex justify-center items-center cursor-wait",
144
144
  )}
145
+ data-testid="spinner"
145
146
  >
146
147
  <div
147
148
  className={classNames(
@@ -164,15 +165,20 @@ export const Loading = () => {
164
165
  classes.loader,
165
166
  "area-[loader] relative overflow-x-clip",
166
167
  )}
168
+ data-testid="loading"
167
169
  >
168
170
  <div className="absolute z-10 py-px inset-x-0 top-0 after:h-0.5 after:block after:bg-primary-500" />
169
171
  </div>
170
172
  );
171
173
  };
172
174
 
173
- export const Skeleton = () => {
175
+ export const Skeleton = ({ className, ...rest }: CssProp) => {
174
176
  return (
175
- <div className={classNames("p-2 animate-pulse flex gap-2 max-w-lg")}>
177
+ <div
178
+ className={classNames(className, "p-2 animate-pulse flex gap-2 max-w-lg")}
179
+ data-testid="skeleton"
180
+ {...rest}
181
+ >
176
182
  <div className="bg-tint-100 rounded aspect-square h-12" />
177
183
  <div className="flex-1 flex flex-col gap-2">
178
184
  <div className="bg-tint-100 h-3 rounded-full" />
@@ -24,8 +24,7 @@
24
24
  import { isSvgPath } from "@react-fabric/utilities";
25
25
  import BoringAvatar from "boring-avatars";
26
26
  import classNames from "classnames";
27
- import { useState } from "react";
28
- import { useMemoDebugger } from "../../hooks/useEffectDebugger";
27
+ import { useMemo, useState } from "react";
29
28
  import {
30
29
  type AriaProps,
31
30
  type ColorType,
@@ -109,89 +108,77 @@ export const Avatar = ({
109
108
  const [fallback, setFallback] = useState(true);
110
109
 
111
110
  /** ***************** style map *******************/
112
- const styles = useMemoDebugger(
113
- () => {
114
- const s: KeyValue = {};
115
- if (bg) {
116
- s.backgroundColor = getColor(bg);
117
- }
118
- if (color) {
119
- s.color = getColor(color);
120
- }
121
- if (size && !(size in SizeMap)) {
122
- s.fontSize = size;
123
- }
124
- if (size && size in SizeMap) {
125
- s.fontSize = SizeMap[size];
126
- }
127
- return s;
128
- },
129
- [bg, color, size],
130
- "Avatar styles",
131
- );
111
+ const styles = useMemo(() => {
112
+ const s: KeyValue = {};
113
+ if (bg) {
114
+ s.backgroundColor = getColor(bg);
115
+ }
116
+ if (color) {
117
+ s.color = getColor(color);
118
+ }
119
+ if (size && !(size in SizeMap)) {
120
+ s.fontSize = size;
121
+ }
122
+ if (size && size in SizeMap) {
123
+ s.fontSize = SizeMap[size];
124
+ }
125
+ return s;
126
+ }, [bg, color, size]);
132
127
 
133
128
  /** ***************** render icon *******************/
134
- const avatarImage = useMemoDebugger(
135
- () => {
136
- setFallback(!avatar);
129
+ const avatarImage = useMemo(() => {
130
+ setFallback(!avatar);
131
+ return (
132
+ avatar && (
133
+ <div className="contents">
134
+ <div
135
+ className={classNames(
136
+ classes.imgPlaceholder,
137
+ "absolute inset-0 bg-gray animate-pulse",
138
+ )}
139
+ />
140
+ <img
141
+ src={avatar}
142
+ alt={avatar}
143
+ className="align-baseline"
144
+ onError={() => setFallback(true)}
145
+ onLoad={(e) => e.currentTarget.previousElementSibling?.remove()}
146
+ />
147
+ </div>
148
+ )
149
+ );
150
+ }, [avatar]);
151
+
152
+ const fallbackAvatar = useMemo(() => {
153
+ if (fallbackIcon) {
154
+ return isSvgPath(fallbackIcon) ? (
155
+ <svg viewBox={viewBox}>
156
+ <path fill="currentColor" d={fallbackIcon.toString()} />
157
+ </svg>
158
+ ) : (
159
+ <i className={fallbackIcon} />
160
+ );
161
+ }
162
+ if (variant === "text") {
163
+ const [, first, second, group] =
164
+ name.match(/(?=(\w).* (\w).*)|(?=(\w\w).*)/) ?? [];
137
165
  return (
138
- avatar && (
139
- <div className="contents">
140
- <div
141
- className={classNames(
142
- classes.imgPlaceholder,
143
- "absolute inset-0 bg-gray animate-pulse",
144
- )}
145
- />
146
- <img
147
- src={avatar}
148
- alt={avatar}
149
- className="align-baseline"
150
- onError={() => setFallback(true)}
151
- onLoad={(e) => e.currentTarget.previousElementSibling?.remove()}
152
- />
153
- </div>
154
- )
166
+ <svg role="img">
167
+ <text
168
+ x="50%"
169
+ y="50%"
170
+ dy=".1em"
171
+ dominantBaseline="middle"
172
+ textAnchor="middle"
173
+ style={{ fontSize: ".5em", fontWeight: 500 }}
174
+ >
175
+ {group ?? `${first}${second}`}
176
+ </text>
177
+ </svg>
155
178
  );
156
- },
157
- [avatar],
158
- "Avatar icon",
159
- );
160
-
161
- const fallbackAvatar = useMemoDebugger(
162
- () => {
163
- if (fallbackIcon) {
164
- return isSvgPath(fallbackIcon) ? (
165
- <svg viewBox={viewBox}>
166
- <path fill="currentColor" d={fallbackIcon.toString()} />
167
- </svg>
168
- ) : (
169
- <i className={fallbackIcon} />
170
- );
171
- }
172
- if (variant === "text") {
173
- const [, first, second, group] =
174
- name.match(/(?=(\w).* (\w).*)|(?=(\w\w).*)/) ?? [];
175
- return (
176
- <svg role="img">
177
- <text
178
- x="50%"
179
- y="50%"
180
- dy=".1em"
181
- dominantBaseline="middle"
182
- textAnchor="middle"
183
- style={{ fontSize: ".5em", fontWeight: 500 }}
184
- >
185
- {group ?? `${first}${second}`}
186
- </text>
187
- </svg>
188
- );
189
- }
190
- return <BoringAvatar square size={64} variant={variant} name={name} />;
191
- },
192
- [name, variant, fallbackIcon],
193
- "Avatar fallback",
194
- );
179
+ }
180
+ return <BoringAvatar square size={64} variant={variant} name={name} />;
181
+ }, [name, variant, fallbackIcon]);
195
182
 
196
183
  return (
197
184
  <dfn
@@ -22,18 +22,19 @@
22
22
  */
23
23
 
24
24
  import {
25
- FloatingArrow,
26
- FloatingFocusManager,
27
- FloatingPortal,
28
25
  arrow,
29
26
  autoUpdate,
30
27
  flip,
28
+ FloatingArrow,
29
+ FloatingFocusManager,
30
+ FloatingPortal,
31
31
  offset,
32
32
  shift,
33
33
  useClick,
34
34
  useDismiss,
35
35
  useFloating,
36
36
  useInteractions,
37
+ type Placement,
37
38
  } from "@floating-ui/react";
38
39
  import { mergeRefs } from "@react-fabric/utilities";
39
40
  import classNames from "classnames";
@@ -66,6 +67,10 @@ interface ExtendedProps extends Omit<BaseProps, "onClick">, RefProp {
66
67
  * ok label
67
68
  */
68
69
  cancelLabel?: string;
70
+ /**
71
+ * dropdown placement
72
+ */
73
+ placement?: Placement;
69
74
  /**
70
75
  * click handler, return Promise to autoset loading state
71
76
  */
@@ -85,6 +90,7 @@ export const ConfirmButton = <Tag extends React.ElementType = "button">({
85
90
  cancelLabel,
86
91
  onClick,
87
92
  loading,
93
+ placement = "bottom",
88
94
  actionMessage,
89
95
  showActionDone,
90
96
  showActionDoneEvent = "click",
@@ -127,7 +133,7 @@ export const ConfirmButton = <Tag extends React.ElementType = "button">({
127
133
  }
128
134
  },
129
135
  strategy: "fixed",
130
- placement: "bottom",
136
+ placement,
131
137
  whileElementsMounted: autoUpdate,
132
138
  middleware: [
133
139
  shift({ padding: 8 }),
@@ -39,6 +39,7 @@ import {
39
39
  type AriaProps,
40
40
  type ChildrenProp,
41
41
  type CssProp,
42
+ type RefProp,
42
43
  type TestProps,
43
44
  } from "../../types";
44
45
  import { nodeCheck } from "../../utils";
@@ -48,6 +49,7 @@ export interface CardProps
48
49
  extends CssProp,
49
50
  AriaProps,
50
51
  TestProps,
52
+ RefProp<HTMLDivElement>,
51
53
  ChildrenProp<React.ReactElement<HeadFootProps | ContentProps>> {
52
54
  /**
53
55
  * apply flex fill styling
@@ -94,6 +96,7 @@ export const Card = ({
94
96
  draggable,
95
97
  dragKey,
96
98
  dragData,
99
+ ref,
97
100
  ...aria
98
101
  }: CardProps) => {
99
102
  const [header, body, footer] = useMemo(() => {
@@ -136,6 +139,7 @@ export const Card = ({
136
139
  dragKey &&
137
140
  e.dataTransfer?.setData(dragKey, JSON.stringify(dragData))
138
141
  }
142
+ ref={ref}
139
143
  {...aria}
140
144
  >
141
145
  {header}
@@ -66,6 +66,10 @@ export interface CollapsableProps
66
66
  * header button className
67
67
  */
68
68
  headerClassName?: string;
69
+ /**
70
+ * Header position sticky
71
+ */
72
+ stickyHeader?: boolean;
69
73
  /**
70
74
  * open state icon
71
75
  */
@@ -100,6 +104,7 @@ export const Collapsable = ({
100
104
  headerClassName,
101
105
  children,
102
106
  iconAlign,
107
+ stickyHeader,
103
108
  iconClosed = CoreIcons.chevronRight,
104
109
  iconOpen = CoreIcons.chevronDown,
105
110
  open,
@@ -138,6 +143,7 @@ export const Collapsable = ({
138
143
  classes.collapsableHead,
139
144
  "flex gap-1 flex-nowrap py-2 font-medium cursor-pointer items-center select-none hover:opacity-70",
140
145
  headerClassName,
146
+ stickyHeader && "bg-base sticky top-0 px-2 py-1 z-10",
141
147
  iconAlign === "end" ? "flex-row-reverse" : "flex-row",
142
148
  )}
143
149
  onClick={() => !disabled && setOpenState(!openState)}
@@ -248,6 +248,7 @@ const DropdownElement = ({
248
248
  zIndex: "var(--z-popover)",
249
249
  ...floatingStyles,
250
250
  }}
251
+ data-testid="dropdown-body"
251
252
  {...getFloatingProps({
252
253
  onClick: (e) => e.stopPropagation(),
253
254
  })}
@@ -31,10 +31,12 @@ export const DropdownTool = ({
31
31
  children,
32
32
  className,
33
33
  groupHover,
34
+ icon,
34
35
  ...props
35
36
  }: ChildProp &
36
37
  CssProp &
37
38
  Omit<DropdownProps, "children"> & {
39
+ icon?: string;
38
40
  /**
39
41
  * hide until group hovered
40
42
  */
@@ -43,7 +45,7 @@ export const DropdownTool = ({
43
45
  return (
44
46
  <Dropdown {...props}>
45
47
  <Icon
46
- icon={CoreIcons.caretDown}
48
+ icon={icon ?? CoreIcons.caretDown}
47
49
  className={classNames(
48
50
  className,
49
51
  "outline bg-base flex-content pointer-events-auto cursor-pointer",
@@ -23,7 +23,7 @@
23
23
 
24
24
  import { isString, isSvgPath } from "@react-fabric/utilities";
25
25
  import classNames from "classnames";
26
- import { useMemoDebugger } from "../../hooks/useEffectDebugger";
26
+ import { useMemo } from "react";
27
27
  import {
28
28
  type AriaProps,
29
29
  type ColorType,
@@ -101,66 +101,54 @@ export const Icon = ({
101
101
  ...rest
102
102
  }: IconProps) => {
103
103
  /** ***************** style map *******************/
104
- const styles = useMemoDebugger(
105
- () => {
106
- const s: KeyValue = {};
107
- if (bg) {
108
- s.backgroundColor = getColor(bg);
109
- }
110
- if (color) {
111
- s.color = getColor(color);
112
- }
113
- if (size && !(size in SizeMap)) {
114
- s.fontSize = size;
115
- }
116
- if (size && size in SizeMap) {
117
- s.fontSize = SizeMap[size];
118
- }
119
- return s;
120
- },
121
- [bg, color, size],
122
- "Icon styles",
123
- );
104
+ const styles = useMemo(() => {
105
+ const s: KeyValue = {};
106
+ if (bg) {
107
+ s.backgroundColor = getColor(bg);
108
+ }
109
+ if (color) {
110
+ s.color = getColor(color);
111
+ }
112
+ if (size && !(size in SizeMap)) {
113
+ s.fontSize = size;
114
+ }
115
+ if (size && size in SizeMap) {
116
+ s.fontSize = SizeMap[size];
117
+ }
118
+ return s;
119
+ }, [bg, color, size]);
124
120
 
125
121
  /** ***************** check if icon is svg path *******************/
126
- const isSvg = useMemoDebugger(
127
- () => {
128
- return isSvgPath(icon);
129
- },
130
- [icon],
131
- "Icon isSvg",
132
- );
122
+ const isSvg = useMemo(() => {
123
+ return isSvgPath(icon);
124
+ }, [icon]);
133
125
 
134
126
  /** ***************** render icon *******************/
135
- const iconEl = useMemoDebugger(
136
- () => {
137
- if (!isString(icon)) {
138
- throw Error("Invalid icon expected string");
139
- }
140
- return isSvg ? (
141
- <svg viewBox={viewBox}>
142
- <path fill="currentColor" d={icon.toString()} />
143
- </svg>
144
- ) : icon?.toString().length <= 4 ? (
145
- <svg role="img">
146
- <text
147
- x="50%"
148
- y="50%"
149
- dy=".1em"
150
- dominantBaseline="middle"
151
- textAnchor="middle"
152
- style={{ fontSize: SvgTextSize[icon.length] ?? "1em" }}
153
- >
154
- {icon}
155
- </text>
156
- </svg>
157
- ) : (
158
- <i className={`${icon}`} />
159
- );
160
- },
161
- [icon, isSvg, viewBox],
162
- "Icon el",
163
- );
127
+ const iconEl = useMemo(() => {
128
+ if (!isString(icon)) {
129
+ throw Error("Invalid icon expected string");
130
+ }
131
+ return isSvg ? (
132
+ <svg viewBox={viewBox}>
133
+ <path fill="currentColor" d={icon.toString()} />
134
+ </svg>
135
+ ) : icon?.toString().length <= 4 ? (
136
+ <svg role="img">
137
+ <text
138
+ x="50%"
139
+ y="50%"
140
+ dy=".1em"
141
+ dominantBaseline="middle"
142
+ textAnchor="middle"
143
+ style={{ fontSize: SvgTextSize[icon.length] ?? "1em" }}
144
+ >
145
+ {icon}
146
+ </text>
147
+ </svg>
148
+ ) : (
149
+ <i className={`${icon}`} />
150
+ );
151
+ }, [icon, isSvg, viewBox]);
164
152
 
165
153
  return (
166
154
  <dfn
@@ -24,12 +24,12 @@
24
24
  import { getImageColorset } from "@react-fabric/utilities";
25
25
  import classNames from "classnames";
26
26
  import { useMemo, useRef, useState } from "react";
27
- import { type AriaProps, type CssProp } from "../../types";
27
+ import { type AriaProps, type CssProp, type TestProps } from "../../types";
28
28
  import { CoreIcons } from "../../types/icons";
29
29
  import { Button } from "../button/Button";
30
30
  import classes from "./Media.module.css";
31
31
 
32
- export interface BaseProps extends CssProp, AriaProps {
32
+ export interface BaseProps extends CssProp, TestProps, AriaProps {
33
33
  /**
34
34
  * media source
35
35
  */