@react-fabric/core 1.0.0-beta.1 → 1.0.0-beta.12

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 (155) hide show
  1. package/css/index.js +13 -13
  2. package/dist/cjs/components/actionLabel/ActionLabel.js +1 -0
  3. package/dist/cjs/components/animations/Animations.js +13 -6
  4. package/dist/cjs/components/avatar/Avatar.js +6 -7
  5. package/dist/cjs/components/badge/Badge.js +6 -4
  6. package/dist/cjs/components/breadcrumbs/Breadcrumbs.js +2 -2
  7. package/dist/cjs/components/button/Button.js +9 -7
  8. package/dist/cjs/components/button/Button.module.css +1 -1
  9. package/dist/cjs/components/button/ConfirmButton.js +4 -2
  10. package/dist/cjs/components/callout/Callout.js +3 -2
  11. package/dist/cjs/components/card/Card.js +4 -2
  12. package/dist/cjs/components/chip/Chip.js +2 -2
  13. package/dist/cjs/components/chip/Chip.module.css +1 -1
  14. package/dist/cjs/components/collapsable/Collapsable.js +5 -3
  15. package/dist/cjs/components/divider/Divider.js +3 -2
  16. package/dist/cjs/components/divider/Divider.module.css +1 -0
  17. package/dist/cjs/components/dropdown/Dropdown.js +4 -2
  18. package/dist/cjs/components/dropdown/DropdownTool.js +3 -2
  19. package/dist/cjs/components/icon/Icon.js +8 -7
  20. package/dist/cjs/components/menu/ContextMenu.js +30 -10
  21. package/dist/cjs/components/menu/Menu.js +1 -1
  22. package/dist/cjs/components/menu/MenuItem.js +1 -1
  23. package/dist/cjs/components/navigator/Navigator.js +15 -9
  24. package/dist/cjs/components/panel/Panel.js +2 -0
  25. package/dist/cjs/components/tabpanel/Tab.js +55 -36
  26. package/dist/cjs/components/tabpanel/TabPanel.js +18 -13
  27. package/dist/cjs/components/tabpanel/TabPanel.module.css +1 -1
  28. package/dist/cjs/core/aside/Aside.js +7 -2
  29. package/dist/cjs/core/content/Content.js +8 -5
  30. package/dist/cjs/core/headfoot/HeadFoot.js +1 -1
  31. package/dist/cjs/core/headfoot/HeadFoot.module.css +1 -1
  32. package/dist/cjs/core/page/Page.js +1 -0
  33. package/dist/cjs/core/section/Section.js +5 -2
  34. package/dist/cjs/hooks/useNavigator.js +6 -4
  35. package/dist/cjs/index.js +3 -2
  36. package/dist/cjs/overlays/alert/Alert.js +38 -16
  37. package/dist/cjs/overlays/modal/Modal.js +1 -1
  38. package/dist/cjs/overlays/tooltip/Tooltip.js +3 -3
  39. package/dist/cjs/overlays/tooltip/Tooltip.module.css +1 -1
  40. package/dist/cjs/types/icons.js +3 -0
  41. package/dist/cjs/typography/Abbr.js +2 -0
  42. package/dist/cjs/typography/{Link.js → Anchor.js} +2 -2
  43. package/dist/cjs/typography/Typography.module.css +1 -1
  44. package/dist/esm/components/actionLabel/ActionLabel.js +1 -0
  45. package/dist/esm/components/animations/Animations.js +13 -6
  46. package/dist/esm/components/avatar/Avatar.js +7 -8
  47. package/dist/esm/components/badge/Badge.js +7 -5
  48. package/dist/esm/components/breadcrumbs/Breadcrumbs.js +2 -2
  49. package/dist/esm/components/button/Button.js +9 -7
  50. package/dist/esm/components/button/Button.module.css +1 -1
  51. package/dist/esm/components/button/ConfirmButton.js +4 -2
  52. package/dist/esm/components/callout/Callout.js +3 -2
  53. package/dist/esm/components/card/Card.js +4 -2
  54. package/dist/esm/components/chip/Chip.js +2 -2
  55. package/dist/esm/components/chip/Chip.module.css +1 -1
  56. package/dist/esm/components/collapsable/Collapsable.js +5 -3
  57. package/dist/esm/components/divider/Divider.js +3 -2
  58. package/dist/esm/components/divider/Divider.module.css +1 -0
  59. package/dist/esm/components/dropdown/Dropdown.js +4 -2
  60. package/dist/esm/components/dropdown/DropdownTool.js +3 -2
  61. package/dist/esm/components/icon/Icon.js +8 -7
  62. package/dist/esm/components/menu/ContextMenu.js +32 -12
  63. package/dist/esm/components/menu/Menu.js +1 -1
  64. package/dist/esm/components/menu/MenuItem.js +1 -1
  65. package/dist/esm/components/navigator/Navigator.js +15 -9
  66. package/dist/esm/components/panel/Panel.js +2 -0
  67. package/dist/esm/components/tabpanel/Tab.js +56 -37
  68. package/dist/esm/components/tabpanel/TabPanel.js +18 -13
  69. package/dist/esm/components/tabpanel/TabPanel.module.css +1 -1
  70. package/dist/esm/core/aside/Aside.js +7 -2
  71. package/dist/esm/core/content/Content.js +8 -5
  72. package/dist/esm/core/headfoot/HeadFoot.js +1 -1
  73. package/dist/esm/core/headfoot/HeadFoot.module.css +1 -1
  74. package/dist/esm/core/page/Page.js +1 -0
  75. package/dist/esm/core/section/Section.js +5 -2
  76. package/dist/esm/hooks/useNavigator.js +7 -5
  77. package/dist/esm/index.js +2 -1
  78. package/dist/esm/overlays/alert/Alert.js +27 -5
  79. package/dist/esm/overlays/modal/Modal.js +1 -1
  80. package/dist/esm/overlays/tooltip/Tooltip.js +3 -3
  81. package/dist/esm/overlays/tooltip/Tooltip.module.css +1 -1
  82. package/dist/esm/types/icons.js +3 -0
  83. package/dist/esm/typography/Abbr.js +2 -0
  84. package/dist/esm/typography/{Link.js → Anchor.js} +2 -2
  85. package/dist/esm/typography/Typography.module.css +1 -1
  86. package/dist/types/components/actionLabel/ActionLabel.d.ts +2 -2
  87. package/dist/types/components/animations/Animations.d.ts +1 -1
  88. package/dist/types/components/breadcrumbs/Breadcrumbs.d.ts +2 -2
  89. package/dist/types/components/button/Button.d.ts +5 -2
  90. package/dist/types/components/button/ConfirmButton.d.ts +6 -1
  91. package/dist/types/components/callout/Callout.d.ts +3 -3
  92. package/dist/types/components/card/Card.d.ts +3 -3
  93. package/dist/types/components/collapsable/Collapsable.d.ts +9 -1
  94. package/dist/types/components/dropdown/DropdownTool.d.ts +2 -1
  95. package/dist/types/components/media/Media.d.ts +2 -2
  96. package/dist/types/components/menu/ContextMenu.d.ts +2 -1
  97. package/dist/types/components/menu/types.d.ts +2 -2
  98. package/dist/types/components/navigator/Navigator.d.ts +3 -3
  99. package/dist/types/components/tabpanel/Tab.d.ts +8 -4
  100. package/dist/types/components/tabpanel/TabPanel.d.ts +10 -1
  101. package/dist/types/core/aside/Aside.d.ts +9 -5
  102. package/dist/types/core/content/Content.d.ts +3 -2
  103. package/dist/types/core/section/Section.d.ts +2 -2
  104. package/dist/types/overlays/alert/Alert.d.ts +9 -1
  105. package/dist/types/overlays/modal/Modal.d.ts +1 -1
  106. package/dist/types/types/icons.d.ts +3 -0
  107. package/dist/types/types/index.d.ts +6 -2
  108. package/dist/types/typography/Abbr.d.ts +3 -3
  109. package/dist/types/typography/{Link.d.ts → Anchor.d.ts} +4 -3
  110. package/dist/types/typography/index.d.ts +1 -1
  111. package/package.json +3 -3
  112. package/src/components/actionLabel/ActionLabel.tsx +13 -3
  113. package/src/components/animations/Animations.tsx +9 -3
  114. package/src/components/avatar/Avatar.tsx +68 -81
  115. package/src/components/badge/Badge.tsx +24 -20
  116. package/src/components/breadcrumbs/Breadcrumbs.tsx +3 -3
  117. package/src/components/button/Button.module.css +1 -1
  118. package/src/components/button/Button.tsx +16 -10
  119. package/src/components/button/ConfirmButton.tsx +10 -4
  120. package/src/components/callout/Callout.tsx +8 -1
  121. package/src/components/card/Card.tsx +4 -0
  122. package/src/components/chip/Chip.module.css +1 -1
  123. package/src/components/chip/Chip.tsx +2 -4
  124. package/src/components/collapsable/Collapsable.tsx +12 -1
  125. package/src/components/divider/Divider.module.css +32 -0
  126. package/src/components/divider/Divider.tsx +3 -2
  127. package/src/components/dropdown/Dropdown.tsx +3 -2
  128. package/src/components/dropdown/DropdownTool.tsx +3 -1
  129. package/src/components/icon/Icon.tsx +46 -57
  130. package/src/components/media/Media.tsx +2 -2
  131. package/src/components/menu/ContextMenu.tsx +36 -11
  132. package/src/components/menu/Menu.tsx +2 -2
  133. package/src/components/menu/types.ts +2 -2
  134. package/src/components/navigator/Navigator.tsx +16 -4
  135. package/src/components/panel/Panel.tsx +2 -0
  136. package/src/components/tabpanel/Tab.tsx +78 -59
  137. package/src/components/tabpanel/TabPanel.module.css +19 -26
  138. package/src/components/tabpanel/TabPanel.tsx +32 -10
  139. package/src/core/aside/Aside.tsx +26 -4
  140. package/src/core/content/Content.tsx +16 -10
  141. package/src/core/headfoot/HeadFoot.module.css +2 -0
  142. package/src/core/headfoot/HeadFoot.tsx +1 -1
  143. package/src/core/page/Page.tsx +1 -0
  144. package/src/core/section/Section.tsx +8 -2
  145. package/src/hooks/useNavigator.tsx +10 -5
  146. package/src/overlays/alert/Alert.tsx +25 -2
  147. package/src/overlays/modal/Modal.tsx +2 -2
  148. package/src/overlays/tooltip/Tooltip.module.css +1 -1
  149. package/src/overlays/tooltip/Tooltip.tsx +4 -4
  150. package/src/types/icons.ts +6 -0
  151. package/src/types/index.ts +10 -2
  152. package/src/typography/Abbr.tsx +4 -3
  153. package/src/typography/{Link.tsx → Anchor.tsx} +6 -3
  154. package/src/typography/Typography.module.css +2 -2
  155. package/src/typography/index.ts +1 -1
@@ -104,7 +104,7 @@ export const AnimationIndicator = ({
104
104
  };
105
105
 
106
106
  export const AnimationBars = ({
107
- color = "tint-700",
107
+ color = "tint",
108
108
  size = "2rem",
109
109
  }: {
110
110
  color?: ColorType | string;
@@ -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
@@ -21,7 +21,7 @@
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 { isNumber, isObject, mergeRefs } from "@react-fabric/utilities";
24
+ import { isNil, isNumber, isObject, mergeRefs } from "@react-fabric/utilities";
25
25
  import classNames from "classnames";
26
26
  import { cloneElement, useMemo } from "react";
27
27
  import {
@@ -101,10 +101,12 @@ export const Badge = ({
101
101
  ...rest
102
102
  }: BadgeProps) => {
103
103
  const label = useMemo(() => {
104
- if (isNumber(value) && max && value > max) {
105
- return `${max}+`;
104
+ if (!isNil(value)) {
105
+ if (isNumber(value) && max && value > max) {
106
+ return `${max}+`;
107
+ }
108
+ return `${value}`;
106
109
  }
107
- return `${value}`;
108
110
  }, [value, max]);
109
111
 
110
112
  const innerRef = useMemo(
@@ -127,22 +129,24 @@ export const Badge = ({
127
129
  ...rest,
128
130
  ref: innerRef,
129
131
  })}
130
- <div
131
- data-ref="badge"
132
- data-ping={ping}
133
- data-placement={placement}
134
- className={classNames(
135
- classes.badge,
136
- className,
137
- "inline-block p-px min-w-2 min-h-2 text-center select-none rounded-full leading-none z-5 pointer-events-none",
138
- forButton && !placement ? "relative" : "absolute",
139
- )}
140
- >
141
- {label && <span className="p-1">{label}</span>}
142
- {icon && (
143
- <Icon icon={icon} bg={iconBg} color={iconColor} rtlFlip={rtlFlip} />
144
- )}
145
- </div>
132
+ {(!!ping || label) && (
133
+ <div
134
+ data-ref="badge"
135
+ data-ping={ping}
136
+ data-placement={placement}
137
+ className={classNames(
138
+ classes.badge,
139
+ className,
140
+ "inline-block p-px min-w-2 min-h-2 text-center select-none rounded-full leading-none z-5 pointer-events-none",
141
+ forButton && !placement ? "relative" : "absolute",
142
+ )}
143
+ >
144
+ {label && <span className="p-1">{label}</span>}
145
+ {icon && (
146
+ <Icon icon={icon} bg={iconBg} color={iconColor} rtlFlip={rtlFlip} />
147
+ )}
148
+ </div>
149
+ )}
146
150
  </div>
147
151
  );
148
152
  };
@@ -24,14 +24,14 @@
24
24
  import classNames from "classnames";
25
25
  import { Children, useMemo } from "react";
26
26
  import { type AriaProps, type ChildrenProp, type CssProp } from "../../types";
27
- import { Link } from "../../typography/Link";
27
+ import { Anchor } from "../../typography/Anchor";
28
28
  import { Dropdown } from "../dropdown/Dropdown";
29
29
  import classes from "./Breadcrumbs.module.css";
30
30
 
31
31
  export interface BreadcrumbProps
32
32
  extends CssProp,
33
33
  AriaProps,
34
- ChildrenProp<typeof Link> {
34
+ ChildrenProp<typeof Anchor> {
35
35
  /**
36
36
  * breadcrumb styling
37
37
  */
@@ -85,7 +85,7 @@ export const Breadcrumbs = ({
85
85
  {overflow && (
86
86
  <Dropdown closeOnClick showArrow>
87
87
  <li className={classes.breadcrumbLink} data-separator={separator}>
88
- <Link>...</Link>
88
+ <Anchor>...</Anchor>
89
89
  </li>
90
90
  <div
91
91
  data-color={color}
@@ -245,7 +245,7 @@
245
245
  background-size: 1em 1em;
246
246
 
247
247
  & :global {
248
- animation: progress 750ms linear infinite;
248
+ animation: bgprogress 750ms linear infinite;
249
249
  }
250
250
  }
251
251
 
@@ -136,6 +136,8 @@ export interface BaseProps
136
136
  */
137
137
  hotKey?: string;
138
138
 
139
+ stopPropagation?: boolean;
140
+
139
141
  onMouseOut?: MouseEventHandler;
140
142
  onMouseOver?: MouseEventHandler;
141
143
  onMouseMove?: MouseEventHandler;
@@ -145,10 +147,14 @@ export interface BaseProps
145
147
  onWheel?: WheelEventHandler;
146
148
  }
147
149
 
148
- export type ButtonProps =
150
+ type ButtonTypeProps =
149
151
  | { children: string | React.ReactElement; "aria-label"?: string }
150
152
  | { children?: never; "aria-label": string };
151
153
 
154
+ export type ButtonProps<Tag extends React.ElementType = "button"> = BaseProps &
155
+ ButtonTypeProps &
156
+ PolymorphicProps<Tag>;
157
+
152
158
  /**
153
159
  * Clickable action button
154
160
  */
@@ -175,6 +181,7 @@ export const Button = <Tag extends React.ElementType = "button">({
175
181
  spinOnHover,
176
182
  onClick,
177
183
  actionMessage,
184
+ stopPropagation,
178
185
  type = "button",
179
186
  hotKey,
180
187
  showActionDone,
@@ -183,7 +190,7 @@ export const Button = <Tag extends React.ElementType = "button">({
183
190
  // @ts-expect-error ignore
184
191
  "data-ref": dataRef,
185
192
  ...aria
186
- }: BaseProps & ButtonProps & PolymorphicProps<Tag>) => {
193
+ }: ButtonProps<Tag>) => {
187
194
  const refEl = useRef<AnyObject>(null);
188
195
  const [busy, setBusy] = useState(false);
189
196
  const [actionDone, setActionDone] = useState(false);
@@ -191,7 +198,7 @@ export const Button = <Tag extends React.ElementType = "button">({
191
198
  const clickHandler = useCallback(
192
199
  (e: React.MouseEvent) => {
193
200
  setBusy(true);
194
- // e.stopPropagation();
201
+ stopPropagation && e.stopPropagation();
195
202
  const ret = onClick?.(e);
196
203
  void Promise.resolve(ret).then((b) => {
197
204
  setBusy(false);
@@ -200,7 +207,7 @@ export const Button = <Tag extends React.ElementType = "button">({
200
207
  }
201
208
  });
202
209
  },
203
- [onClick, disabled, actionMessage, showActionDoneEvent],
210
+ [onClick, disabled, actionMessage, showActionDoneEvent, stopPropagation],
204
211
  );
205
212
 
206
213
  /** ***************** reset done state on timeout *******************/
@@ -237,6 +244,10 @@ export const Button = <Tag extends React.ElementType = "button">({
237
244
  return getBadgeProps(badge);
238
245
  }, [badge]);
239
246
 
247
+ const hotKeyHandler = useRef(() => {
248
+ !disabled && refEl.current?.click();
249
+ });
250
+
240
251
  const E = as ?? "button";
241
252
  return (
242
253
  <div
@@ -251,12 +262,7 @@ export const Button = <Tag extends React.ElementType = "button">({
251
262
  data-loading={busy || loading}
252
263
  data-disabled={disabled}
253
264
  >
254
- {hotKey && (
255
- <HotKey
256
- keyCombo={hotKey}
257
- handler={() => !disabled && refEl.current?.click()}
258
- />
259
- )}
265
+ {hotKey && <HotKey keyCombo={hotKey} handler={hotKeyHandler.current} />}
260
266
  <TooltipWrapper {...tooltipProps}>
261
267
  <E
262
268
  ref={mergeRefs(ref, refEl)}
@@ -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 }),
@@ -26,6 +26,7 @@ import {
26
26
  type AriaProps,
27
27
  type ChildrenProp,
28
28
  type ColorType,
29
+ type CssProp,
29
30
  type PaletteType,
30
31
  type TestProps,
31
32
  } from "../../types";
@@ -33,7 +34,11 @@ import { CoreIcons } from "../../types/icons";
33
34
  import { getBgClass, getBorderClass, getColorClass } from "../../utils";
34
35
  import { Icon } from "../icon/Icon";
35
36
 
36
- export interface CalloutProps extends AriaProps, ChildrenProp, TestProps {
37
+ export interface CalloutProps
38
+ extends AriaProps,
39
+ ChildrenProp,
40
+ CssProp,
41
+ TestProps {
37
42
  /**
38
43
  * callout color
39
44
  */
@@ -69,11 +74,13 @@ export const Callout = ({
69
74
  icon,
70
75
  border,
71
76
  onClose,
77
+ className,
72
78
  ...aria
73
79
  }: CalloutProps) => {
74
80
  return (
75
81
  <fieldset
76
82
  className={classNames(
83
+ className,
77
84
  "block rounded-capped border max-w-full relative overflow-hidden mb-4",
78
85
  getBgClass(color + "-50"),
79
86
  getColorClass(color + "-700"),
@@ -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}
@@ -31,7 +31,7 @@
31
31
  outline-offset: -1px;
32
32
 
33
33
  & > .chipLabel {
34
- padding: 0.25em 0.5em;
34
+ padding: 0 0.5em;
35
35
  }
36
36
 
37
37
  & > .chipIcon {
@@ -156,7 +156,7 @@ export const Chip = <Tag extends React.ElementType = "div">({
156
156
  classes.chip,
157
157
  className,
158
158
  color && getColorClass(color),
159
- "select-none inline-flex flex-nowrap items-center max-w-72 text-sm leading-none rounded",
159
+ "select-none inline-flex flex-nowrap items-center max-w-72 text-sm rounded",
160
160
  rounded && "rounded-full",
161
161
  !!onClick && classes.clickable,
162
162
  disabled && classes.disabled,
@@ -178,9 +178,7 @@ export const Chip = <Tag extends React.ElementType = "div">({
178
178
  />
179
179
  )}
180
180
  {children && (
181
- <label
182
- className={classNames(classes.chipLabel, "leading-none truncate")}
183
- >
181
+ <label className={classNames(classes.chipLabel, "truncate")}>
184
182
  {children}
185
183
  </label>
186
184
  )}
@@ -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
  */
@@ -78,6 +82,10 @@ export interface CollapsableProps
78
82
  * open state
79
83
  */
80
84
  open?: boolean;
85
+ /**
86
+ * disable collapsable
87
+ */
88
+ disabled?: boolean;
81
89
  /**
82
90
  * open handler
83
91
  */
@@ -90,11 +98,13 @@ export interface CollapsableProps
90
98
 
91
99
  export const Collapsable = ({
92
100
  ref,
101
+ disabled,
93
102
  className,
94
103
  bodyClassName,
95
104
  headerClassName,
96
105
  children,
97
106
  iconAlign,
107
+ stickyHeader,
98
108
  iconClosed = CoreIcons.chevronRight,
99
109
  iconOpen = CoreIcons.chevronDown,
100
110
  open,
@@ -133,9 +143,10 @@ export const Collapsable = ({
133
143
  classes.collapsableHead,
134
144
  "flex gap-1 flex-nowrap py-2 font-medium cursor-pointer items-center select-none hover:opacity-70",
135
145
  headerClassName,
146
+ stickyHeader && "bg-base sticky top-0 px-2 py-1 z-10",
136
147
  iconAlign === "end" ? "flex-row-reverse" : "flex-row",
137
148
  )}
138
- onClick={() => setOpenState(!openState)}
149
+ onClick={() => !disabled && setOpenState(!openState)}
139
150
  >
140
151
  <Icon size="1.125em" icon={openState ? iconOpen : iconClosed} rtlFlip />
141
152
  <div className="flex-1 px-2">{head}</div>
@@ -0,0 +1,32 @@
1
+ /*
2
+ * React Fabric
3
+ * @version: 1.0.0
4
+ *
5
+ *
6
+ * The MIT License (MIT)
7
+ * Copyright (c) 2024 Adarsh Pastakia
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
10
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
11
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
13
+ * subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
16
+ * portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+
24
+ @layer comp {
25
+ .vertical {
26
+ margin-inline: 0.25rem;
27
+ }
28
+
29
+ .horizontal {
30
+ margin-block: 0.25rem;
31
+ }
32
+ }
@@ -30,6 +30,7 @@ import {
30
30
  type PaletteType,
31
31
  } from "../../types";
32
32
  import { getBgClass, getBorderClass, getColorClass } from "../../utils";
33
+ import classes from "./Divider.module.css";
33
34
 
34
35
  export interface DividerProps extends CssProp, Partial<ChildProp> {
35
36
  /**
@@ -67,7 +68,7 @@ export const Divider = ({
67
68
  className,
68
69
  vertical && "vertical",
69
70
  !flex && "self-stretch",
70
- vertical ? "mx-1" : "my-1",
71
+ vertical ? classes.vertical : classes.horizontal,
71
72
  color && getBorderClass(color + "-200"),
72
73
  )}
73
74
  />
@@ -88,7 +89,7 @@ export const Divider = ({
88
89
  <div
89
90
  className={classNames(
90
91
  "px-2 whitespace-nowrap",
91
- vertical ? "mx-1" : "my-1",
92
+ vertical ? classes.vertical : classes.horizontal,
92
93
  fill && getBgClass((color ?? "tint") + "-200"),
93
94
  !fill && getColorClass((color ?? "tint") + "-600"),
94
95
  "rounded-full text-[0.75em]",
@@ -224,6 +224,7 @@ const DropdownElement = ({
224
224
  e.stopPropagation();
225
225
  },
226
226
  }),
227
+ "data-inner-clickable": "true",
227
228
  "data-dropdown-open": isOpen ? true : undefined,
228
229
  ref: innerRef,
229
230
  })}
@@ -248,6 +249,7 @@ const DropdownElement = ({
248
249
  zIndex: "var(--z-popover)",
249
250
  ...floatingStyles,
250
251
  }}
252
+ data-testid="dropdown-body"
251
253
  {...getFloatingProps({
252
254
  onClick: (e) => e.stopPropagation(),
253
255
  })}
@@ -257,8 +259,7 @@ const DropdownElement = ({
257
259
  className={classNames(
258
260
  classes.dropdownBody,
259
261
  dropdownClassName,
260
- !plainDropdown &&
261
- "shadow-lg shadow-base bg-base ring-1 ring-tint-100",
262
+ !plainDropdown && "shadow-lg bg-base ring-1 ring-tint-100",
262
263
  "rounded-capped overflow-auto scroll-thin grid max-h-[70vh]",
263
264
  )}
264
265
  onMouseUpCapture={tryClosing}