@luscii-healthtech/web-ui 30.2.0 → 30.3.1

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.
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- type AvatarColor = "primary";
2
+ type AvatarColor = "primary" | "slate";
3
3
  type AvatarSize = "large" | "medium" | "small" | "xsmall";
4
4
  type Props = {
5
5
  color?: AvatarColor;
@@ -20,6 +20,11 @@ type Props = {
20
20
  */
21
21
  defaultImage?: string;
22
22
  className?: string;
23
+ /**
24
+ * Sometimes avatars are rendered in a group. Setting this to `true`
25
+ * makes it so that the avatars overlap each other a bit, as designed.
26
+ */
27
+ applyGroupOffset?: boolean;
23
28
  };
24
- export declare const Avatar: ({ color, size, src, alt, defaultImage, initials, className, }: Props) => React.JSX.Element;
29
+ export declare const Avatar: ({ color, size, applyGroupOffset, src, alt, defaultImage, initials, className, }: Props) => React.JSX.Element;
25
30
  export {};
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { StackProps } from "../Stack/Stack";
3
+ export type Props<C extends React.ElementType> = React.ComponentPropsWithoutRef<C> & StackProps<C> & {
4
+ as?: C;
5
+ children?: React.ReactNode;
6
+ /**
7
+ * In a group each avatar receives a border that
8
+ * matches the background the group is on. This to
9
+ * create some separation between the avatars.
10
+ * In case your background is not white, you might
11
+ * want to specify a different color.
12
+ *
13
+ * This color will be added to a Tailwind class in
14
+ * the form of `ui-outline-{outlineColor}`.
15
+ */
16
+ outlineColor?: "surface" | "slate-50" | "slate-100" | "white";
17
+ /**
18
+ * The maximum number of avatars to show in the group, before
19
+ * we render an extra element that shows there are more avatars.
20
+ *
21
+ * @default 3
22
+ */
23
+ maxAvatars?: number;
24
+ /**
25
+ * The label to show when there are more avatars than the
26
+ * `maxAvatars` prop.
27
+ * This function receives the amount of avatars that are not shown.
28
+ */
29
+ moreAvatarsLabel?: (avatarsNotShownCount: number) => React.ReactNode;
30
+ };
31
+ export declare const AvatarGroup: <C extends React.ElementType<any>>(props: Props<C>) => React.JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import React, { ComponentProps } from "react";
2
2
  import { SIZES } from "../../system/Sizes";
3
3
  import { Box } from "../Box/Box";
4
+ export { Props as StackProps };
4
5
  type Size = keyof typeof SIZES;
5
6
  type Props<C extends React.ElementType> = React.ComponentPropsWithoutRef<C> & {
6
7
  as?: C;
@@ -42,4 +43,3 @@ type Props<C extends React.ElementType> = React.ComponentPropsWithoutRef<C> & {
42
43
  className?: string;
43
44
  };
44
45
  export declare const Stack: <C extends React.ElementType = "div">(props: Props<C> & ComponentProps<typeof Box>) => React.JSX.Element;
45
- export {};
@@ -30,6 +30,11 @@ export declare const allowedColors: {
30
30
  readonly white: "ui-text-white";
31
31
  readonly "blue-800": "ui-text-primary";
32
32
  readonly current: "ui-text-current";
33
+ /**
34
+ * Semantic colors
35
+ */
36
+ readonly "on-surface": "ui-text-on-surface";
37
+ readonly "on-surface-variant": "ui-text-on-surface-variant";
33
38
  };
34
39
  declare const allowedHoverColors: {
35
40
  readonly "blue-900": "hover:ui-text-primary-dark";
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export { type ModalSize, type ModalBaseProps, } from "./components/Modal/ModalBa
17
17
  export { type ModalHeaderProps } from "./components/Modal/ModalHeader";
18
18
  export { type ModalFooterProps, type ModalFooterLeadingComponent, type ModalFooterTrailingComponents, } from "./components/Modal/ModalFooter";
19
19
  export { Avatar } from "./components/Avatar/Avatar";
20
+ export { AvatarGroup } from "./components/Avatar/AvatarGroup";
20
21
  export { default as Badge } from "./components/Badge/Badge";
21
22
  export { type BaseButtonProps as NonPrimaryButtonProps, type ButtonWithPendingStateProps as PrimaryButtonProps, type ButtonDefinition, } from "./components/ButtonV2/ButtonProps.type";
22
23
  export { PrimaryButton } from "./components/ButtonV2/PrimaryButton";
@@ -975,7 +975,12 @@ const allowedColors = {
975
975
  amber: "ui-text-yellow-800",
976
976
  white: "ui-text-white",
977
977
  "blue-800": "ui-text-primary",
978
- current: "ui-text-current"
978
+ current: "ui-text-current",
979
+ /**
980
+ * Semantic colors
981
+ */
982
+ "on-surface": "ui-text-on-surface",
983
+ "on-surface-variant": "ui-text-on-surface-variant"
979
984
  };
980
985
  const allowedHoverColors = {
981
986
  "blue-900": "hover:ui-text-primary-dark",
@@ -2537,7 +2542,7 @@ function checkImageValidity(src) {
2537
2542
  image.src = src;
2538
2543
  });
2539
2544
  }
2540
- const Avatar = ({ color, size = "medium", src, alt, defaultImage, initials, className }) => {
2545
+ const Avatar = ({ color = "slate", size = "medium", applyGroupOffset, src, alt, defaultImage, initials, className }) => {
2541
2546
  const [hasValidImage, setHasValidImage] = React.useState(false);
2542
2547
  React.useEffect(() => {
2543
2548
  let mounted = true;
@@ -2558,11 +2563,17 @@ const Avatar = ({ color, size = "medium", src, alt, defaultImage, initials, clas
2558
2563
  "ui-h-9 ui-w-9": size === "small",
2559
2564
  "ui-h-6 ui-w-6": size === "xsmall"
2560
2565
  });
2566
+ const groupOffsetClassNames = classNames__default.default({
2567
+ "-ui-ml-[calc(theme('spacing.32')/4)]": size === "large" && applyGroupOffset,
2568
+ "-ui-ml-[calc(theme('spacing.16')/4)]": size === "medium" && applyGroupOffset,
2569
+ "-ui-ml-[calc(theme('spacing.9')/4)]": size === "small" && applyGroupOffset,
2570
+ "-ui-ml-[calc(theme('spacing.6')/4)]": size === "xsmall" && applyGroupOffset
2571
+ });
2561
2572
  const colorClassName = classNames__default.default({
2562
- "ui-bg-slate-100 ui-text-slate-800": !color,
2573
+ "ui-bg-slate-100 ui-text-on-surface": color === "slate",
2563
2574
  "ui-bg-primary ui-text-white": color === "primary"
2564
2575
  });
2565
- const containerClassName = classNames__default.default(className, sizeClassName, colorClassName, "ui-flex ui-items-center ui-justify-center");
2576
+ const containerClassName = classNames__default.default(className, sizeClassName, groupOffsetClassNames, colorClassName, "ui-flex ui-items-center ui-justify-center");
2566
2577
  const textClassName = classNames__default.default({
2567
2578
  "ui-text-5xl": size === "large",
2568
2579
  "ui-text-2xl": size === "medium",
@@ -2575,11 +2586,19 @@ const Avatar = ({ color, size = "medium", src, alt, defaultImage, initials, clas
2575
2586
  } else if (hasValidImage) {
2576
2587
  return React__namespace.default.createElement("img", { className: imageClassName, src, alt });
2577
2588
  } else {
2589
+ if (size === "xsmall") {
2590
+ return React__namespace.default.createElement(
2591
+ React__namespace.default.Fragment,
2592
+ null,
2593
+ React__namespace.default.createElement(Text, { variant: "sm", color: "current", "aria-hidden": true }, initials),
2594
+ React__namespace.default.createElement(ScreenReaderText, null, alt)
2595
+ );
2596
+ }
2578
2597
  return React__namespace.default.createElement(
2579
2598
  React__namespace.default.Fragment,
2580
2599
  null,
2581
2600
  React__namespace.default.createElement("span", { className: textClassName, "aria-hidden": true }, initials),
2582
- React__namespace.default.createElement("span", { className: "ui-sr-only" }, alt)
2601
+ React__namespace.default.createElement(ScreenReaderText, null, alt)
2583
2602
  );
2584
2603
  }
2585
2604
  };
@@ -2589,6 +2608,56 @@ const Avatar = ({ color, size = "medium", src, alt, defaultImage, initials, clas
2589
2608
  React__namespace.default.createElement("div", null, renderAvatar())
2590
2609
  );
2591
2610
  };
2611
+ function ScreenReaderText({ children }) {
2612
+ return React__namespace.default.createElement("span", { className: "ui-sr-only" }, children);
2613
+ }
2614
+
2615
+ const AvatarGroup = (props) => {
2616
+ const { children, maxAvatars = 3 } = props;
2617
+ return React__namespace.default.createElement(
2618
+ Stack,
2619
+ Object.assign({ axis: "x", align: "center" }, props),
2620
+ React__namespace.default.Children.map(children, (child, index) => {
2621
+ if (!React__namespace.default.isValidElement(child)) {
2622
+ console.error(`AvatarGroup only accepts children of type Avatar, but received an invalid React element.`);
2623
+ return null;
2624
+ }
2625
+ if (child.type !== Avatar) {
2626
+ console.error(`AvatarGroup only accepts children of type Avatar, but received ${child.type}.`);
2627
+ return null;
2628
+ }
2629
+ if (index + 1 > maxAvatars) {
2630
+ return null;
2631
+ }
2632
+ return React__namespace.default.createElement(Avatar, Object.assign({}, child.props, { applyGroupOffset: index > 0, className: classNames__default.default(child.props.className, `ui-outline`, {
2633
+ "ui-outline-surface": !props.outlineColor || props.outlineColor === "surface",
2634
+ "ui-outline-slate-50": props.outlineColor === "slate-50",
2635
+ "ui-outline-slate-100": props.outlineColor === "slate-100",
2636
+ "ui-outline-white": props.outlineColor === "white"
2637
+ }, {
2638
+ "ui-outline-2": child.props.size === "xsmall" || child.props.size === "small",
2639
+ "ui-outline-4": child.props.size === "medium",
2640
+ "ui-outline-8": child.props.size === "large"
2641
+ }) }));
2642
+ }),
2643
+ React__namespace.default.createElement(MoreAvatarsLabel, Object.assign({}, props, { maxAvatars }))
2644
+ );
2645
+ };
2646
+ function MoreAvatarsLabel(props) {
2647
+ const { children, maxAvatars, moreAvatarsLabel } = props;
2648
+ if (!moreAvatarsLabel) {
2649
+ return null;
2650
+ }
2651
+ const amountOfAvatars = React__namespace.default.Children.count(children);
2652
+ if (amountOfAvatars <= maxAvatars) {
2653
+ return null;
2654
+ }
2655
+ return React__namespace.default.createElement(
2656
+ Box,
2657
+ { ml: "xxxxs" },
2658
+ React__namespace.default.createElement(Text, { variant: "medium", color: "on-surface-variant" }, moreAvatarsLabel(amountOfAvatars - maxAvatars))
2659
+ );
2660
+ }
2592
2661
 
2593
2662
  const Badge = (props) => {
2594
2663
  var _a;
@@ -5300,13 +5369,13 @@ const ImageCategory = (props) => {
5300
5369
  React__namespace.default.createElement("div", { className: "ui-flex ui-flex-row ui-flex-wrap" }, props.images.map((option, index) => {
5301
5370
  const isSelected = option === props.highlightedImage;
5302
5371
  return React__namespace.default.createElement(
5303
- "div",
5304
- { "data-test-id": `image-from-selector-${index.toString().padStart(2, "0")}`, key: option, role: "button", className: classNames__default.default("ui-m-2 ui-rounded ui-border ui-border-solid ui-border-slate-300 ui-p-2", {
5372
+ "button",
5373
+ { "data-test-id": `image-from-selector-${index.toString().padStart(2, "0")}`, key: option, "data-index": index, "data-src": option, onClick: props.handleImageClick, className: classNames__default.default("ui-m-2 ui-rounded ui-border ui-border-solid ui-border-slate-300 ui-p-2", {
5305
5374
  "ui-outline ui-outline-primary": isSelected,
5306
5375
  "ui-h-11 ui-w-11": props.isTypeCompact,
5307
5376
  "ui-h-22 ui-w-36": !props.isTypeCompact
5308
5377
  }) },
5309
- React__namespace.default.createElement(Image$2, { className: "ui-h-full ui-w-full ui-rounded", onClick: props.handleImageClick, src: option, "data-index": index, wrapperClassName: classNames__default.default({
5378
+ React__namespace.default.createElement(Image$2, { className: "ui-h-full ui-w-full ui-rounded", src: option, wrapperClassName: classNames__default.default({
5310
5379
  "ui-h-full ui-w-full": !props.isTypeCompact
5311
5380
  }) })
5312
5381
  );
@@ -5369,8 +5438,11 @@ const ImagePickerInner = ({
5369
5438
  if (datasetIndex && parseInt(datasetIndex, 10) === clearImageIndex) {
5370
5439
  handleDeleteClick();
5371
5440
  }
5372
- setHighlightedImage(event.currentTarget.src);
5373
- setError(null);
5441
+ const src = event.currentTarget.dataset["src"];
5442
+ if (src) {
5443
+ setHighlightedImage(src);
5444
+ setError(null);
5445
+ }
5374
5446
  };
5375
5447
  const handleDeleteClick = () => {
5376
5448
  handleChange({ target: { name, value: "" } });
@@ -6589,6 +6661,7 @@ exports.AlertsIcon = BellIcon;
6589
6661
  exports.AmberAlertIcon = AmberAlertIcon;
6590
6662
  exports.AssignedIcon = AssignedIcon;
6591
6663
  exports.Avatar = Avatar;
6664
+ exports.AvatarGroup = AvatarGroup;
6592
6665
  exports.Badge = Badge;
6593
6666
  exports.BellIcon = BellIcon;
6594
6667
  exports.BellIconSlashed = BellIconSlashed;