margo-ui 5.2.0 → 6.0.0

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 (46) hide show
  1. package/package.json +2 -2
  2. package/src/components/blockquote/style.ts +1 -1
  3. package/src/components/button/button.tsx +13 -16
  4. package/src/components/button/style.ts +1 -1
  5. package/src/components/button/type.ts +4 -5
  6. package/src/components/button_micro/button_micro.tsx +12 -26
  7. package/src/components/button_micro/type.ts +1 -3
  8. package/src/components/callout/callout.tsx +15 -0
  9. package/src/components/callout/style.ts +4 -0
  10. package/src/components/callout/type.ts +9 -0
  11. package/src/components/card/style.ts +1 -1
  12. package/src/components/chip/chip.tsx +1 -1
  13. package/src/components/code/code.tsx +7 -5
  14. package/src/components/header/header.tsx +3 -8
  15. package/src/components/header/type.ts +1 -1
  16. package/src/components/input/input.tsx +2 -2
  17. package/src/components/input/type.ts +1 -1
  18. package/src/components/item/item.tsx +10 -13
  19. package/src/components/item/style.ts +1 -1
  20. package/src/components/item/type.ts +2 -3
  21. package/src/components/layer/layer.tsx +15 -1
  22. package/src/components/popover/popover.tsx +4 -4
  23. package/src/components/popover/style.ts +7 -7
  24. package/src/components/popover/type.ts +2 -2
  25. package/src/components/progress_bar/style.ts +2 -1
  26. package/src/components/sheet/sheet.css +8 -8
  27. package/src/components/sheet/sheet.tsx +9 -4
  28. package/src/components/sheet/style.ts +4 -4
  29. package/src/components/sheet/type.ts +2 -2
  30. package/src/components/table/style.ts +2 -1
  31. package/src/components/text_area/style.ts +10 -0
  32. package/src/components/text_area/text_area.tsx +25 -0
  33. package/src/components/text_area/type.ts +11 -0
  34. package/src/components/tooltip/style.ts +6 -6
  35. package/src/components/tooltip/tooltip.tsx +3 -3
  36. package/src/components/tooltip/type.ts +2 -2
  37. package/src/css/base.css +0 -14
  38. package/src/css/theme.css +1 -2
  39. package/src/hoc/background_glow/background_glow.css +3 -5
  40. package/src/hoc/border_glow/border_glow.tsx +8 -5
  41. package/src/hoc/border_glow/style.ts +4 -4
  42. package/src/hoc/border_glow/type.ts +2 -2
  43. package/src/hoc/pointer_holder/pointer_holder.tsx +5 -1
  44. package/src/hoc/pointer_holder/type.ts +1 -3
  45. package/src/index.ts +15 -5
  46. package/src/utils/cn/cn.ts +10 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "margo-ui",
3
- "version": "5.2.0",
4
- "description": "React UI kit built on Tailwind CSS v4 design tokens, themeable through plain CSS custom properties.",
3
+ "version": "6.0.0",
4
+ "description": "margo-ui is a React component kit built on Tailwind CSS v4, with compound components and overridable CSS tokens.",
5
5
  "keywords": [
6
6
  "react",
7
7
  "ui",
@@ -1 +1 @@
1
- export const blockquoteBaseClassName = "border-l-margo border-primary-darken py-0.5 pl-4 flex items-center";
1
+ export const blockquoteBaseClassName = "border-l-margo border-primary-darken py-0.5 pl-4 flex flex-col justify-center";
@@ -23,19 +23,15 @@ export const Button = <T extends ElementType = "button">({
23
23
  disabled = false,
24
24
  active = false,
25
25
  open = false,
26
- onClickBlur,
27
26
  className,
28
27
  onClick,
29
28
  ...rest
30
29
  }: ButtonProps<T>) => {
30
+ const isNativeButton = (rest.as ?? "button") === "button";
31
+
31
32
  const handleClick = (event: MouseEvent<HTMLElement>) => {
32
- if (disabled) return;
33
+ if (disabled) return event.preventDefault();
33
34
  onClick?.(event);
34
- if (typeof onClickBlur === "function") {
35
- const target = event.currentTarget;
36
- onClickBlur(event);
37
- setTimeout(() => target.blur(), 150);
38
- }
39
35
  };
40
36
 
41
37
  return (
@@ -44,9 +40,10 @@ export const Button = <T extends ElementType = "button">({
44
40
  {...Tag.forward<T>(rest, "button")}
45
41
  data-margo-open={open}
46
42
  data-margo-disabled={disabled}
47
- aria-disabled={disabled || undefined}
48
- data-active={active || undefined}
49
- inert={disabled || !clickable || undefined}
43
+ data-margo-active={active}
44
+ disabled={(isNativeButton && disabled) || undefined}
45
+ aria-disabled={(!isNativeButton && disabled) || undefined}
46
+ inert={!clickable || undefined}
50
47
  onClick={handleClick}
51
48
  className={cn(
52
49
  buttonBaseClassName,
@@ -59,12 +56,12 @@ export const Button = <T extends ElementType = "button">({
59
56
  );
60
57
  };
61
58
 
62
- Button.Icon = ({ icon, className }: ButtonIconProps) => (
63
- <span className={cn(buttonIconClassName, className)}>{icon}</span>
59
+ Button.Icon = ({ children, className }: ButtonIconProps) => (
60
+ <span className={cn(buttonIconClassName, className)}>{children}</span>
64
61
  );
65
62
 
66
- Button.Label = ({ label, className }: ButtonLabelProps) => (
67
- <span className={cn(buttonLabelClassName, className)}>{label}</span>
63
+ Button.Label = ({ children, className }: ButtonLabelProps) => (
64
+ <span className={cn(buttonLabelClassName, className)}>{children}</span>
68
65
  );
69
66
 
70
67
  Button.IconLabel = ({
@@ -72,14 +69,14 @@ Button.IconLabel = ({
72
69
  label,
73
70
  gap = "0.5rem",
74
71
  reverse = false,
75
- side = "end",
72
+ direction = "right",
76
73
  className,
77
74
  }: ButtonIconLabelProps) => (
78
75
  <span
79
76
  style={{ "--margo-button-gap": gap } as CSSProperties}
80
77
  className={cn(
81
78
  buttonGridClassName,
82
- side === "start" && "[direction:rtl]",
79
+ direction === "left" && "[direction:rtl]",
83
80
  reverse ? buttonGridReverseClassName : buttonGridForwardClassName,
84
81
  className,
85
82
  )}
@@ -7,7 +7,7 @@ export const buttonSurfaceClassName = "border-border";
7
7
 
8
8
  export const buttonActiveClassName = "border-primary shadow-button";
9
9
 
10
- export const buttonDisabledClassName = "opacity-40";
10
+ export const buttonDisabledClassName = "pointer-events-none opacity-40";
11
11
 
12
12
  export const buttonIconClassName =
13
13
  "relative z-10 flex size-4 min-w-0 shrink-0 items-center justify-center [direction:ltr]";
@@ -1,7 +1,7 @@
1
1
  import type { TagProps } from "react-renderable";
2
2
  import type { ElementType, MouseEventHandler, ReactNode } from "react";
3
3
 
4
- export type ButtonIconLabelSide = "start" | "end";
4
+ export type ButtonIconLabelDirection = "left" | "right";
5
5
 
6
6
  export type ButtonOwnProps = {
7
7
  clickable?: boolean;
@@ -9,18 +9,17 @@ export type ButtonOwnProps = {
9
9
  active?: boolean;
10
10
  open?: boolean;
11
11
  onClick?: MouseEventHandler<HTMLElement>;
12
- onClickBlur?: MouseEventHandler<HTMLElement>;
13
12
  };
14
13
 
15
14
  export type ButtonProps<T extends ElementType = "button"> = TagProps<T, ButtonOwnProps>;
16
15
 
17
16
  export type ButtonIconProps = {
18
- icon: ReactNode;
17
+ children: ReactNode;
19
18
  className?: string;
20
19
  };
21
20
 
22
21
  export type ButtonLabelProps = {
23
- label: string;
22
+ children: ReactNode;
24
23
  className?: string;
25
24
  };
26
25
 
@@ -29,6 +28,6 @@ export type ButtonIconLabelProps = {
29
28
  label: ReactNode;
30
29
  gap?: string;
31
30
  reverse?: boolean;
32
- side?: ButtonIconLabelSide;
31
+ direction?: ButtonIconLabelDirection;
33
32
  className?: string;
34
33
  };
@@ -3,35 +3,21 @@ import { Tag } from "react-renderable";
3
3
  import { cn } from "../../utils/cn/cn.js";
4
4
  import { buttonMicroActiveClassName, buttonMicroBaseClassName, buttonMicroSurfaceClassName } from "./style.js";
5
5
 
6
- import type { ElementType, MouseEvent } from "react";
6
+ import type { ElementType } from "react";
7
7
  import type { ButtonMicroProps } from "./type.js";
8
8
 
9
9
  export const ButtonMicro = <T extends ElementType = "button">({
10
10
  active = false,
11
- onClickBlur,
12
- onClick,
13
11
  className,
14
12
  ...rest
15
- }: ButtonMicroProps<T>) => {
16
- const handleClick = (event: MouseEvent<HTMLElement>) => {
17
- onClick?.(event);
18
- if (typeof onClickBlur === "function") {
19
- const target = event.currentTarget;
20
- onClickBlur(event);
21
- setTimeout(() => target.blur(), 150);
22
- }
23
- };
24
-
25
- return (
26
- <Tag
27
- {...Tag.forward<T>(rest, "button")}
28
- data-margo-active={active}
29
- onClick={handleClick}
30
- className={cn(
31
- buttonMicroBaseClassName,
32
- active ? buttonMicroActiveClassName : buttonMicroSurfaceClassName,
33
- className,
34
- )}
35
- />
36
- );
37
- };
13
+ }: ButtonMicroProps<T>) => (
14
+ <Tag
15
+ {...Tag.forward<T>(rest, "button")}
16
+ data-margo-active={active}
17
+ className={cn(
18
+ buttonMicroBaseClassName,
19
+ active ? buttonMicroActiveClassName : buttonMicroSurfaceClassName,
20
+ className,
21
+ )}
22
+ />
23
+ );
@@ -1,10 +1,8 @@
1
1
  import type { TagProps } from "react-renderable";
2
- import type { ElementType, MouseEventHandler } from "react";
2
+ import type { ElementType } from "react";
3
3
 
4
4
  export type ButtonMicroOwnProps = {
5
5
  active?: boolean;
6
- onClick?: MouseEventHandler<HTMLElement>;
7
- onClickBlur?: MouseEventHandler<HTMLElement>;
8
6
  };
9
7
 
10
8
  export type ButtonMicroProps<T extends ElementType = "button"> = TagProps<T, ButtonMicroOwnProps>;
@@ -0,0 +1,15 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { calloutBaseClassName, calloutTitleClassName } from "./style.js";
5
+
6
+ import type { ElementType } from "react";
7
+ import type { CalloutProps, CalloutTitleProps } from "./type.js";
8
+
9
+ export const Callout = <T extends ElementType = "aside">({ className, ...rest }: CalloutProps<T>) => (
10
+ <Tag {...Tag.forward<T>(rest, "aside")} className={cn(calloutBaseClassName, className)} />
11
+ );
12
+
13
+ Callout.Title = ({ children, className }: CalloutTitleProps) => (
14
+ <span className={cn(calloutTitleClassName, className)}>{children}</span>
15
+ );
@@ -0,0 +1,4 @@
1
+ export const calloutBaseClassName =
2
+ "space-y-2 rounded-margo-base border-l-margo border-primary bg-low px-4 py-3 text-on-main";
3
+
4
+ export const calloutTitleClassName = "block text-mc uppercase tracking-widest text-primary";
@@ -0,0 +1,9 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ElementType, ReactNode } from "react";
3
+
4
+ export type CalloutProps<T extends ElementType = "aside"> = TagProps<T>;
5
+
6
+ export type CalloutTitleProps = {
7
+ children: ReactNode;
8
+ className?: string;
9
+ };
@@ -1,4 +1,4 @@
1
1
  export const cardBaseClassName = "rounded-margo-base border-margo border-border bg-main p-5 text-on-main shadow-card";
2
2
 
3
- export const cardFocusClassName = `focus:border-primary focus-visible:outline focus-visible:outline-offset-1
3
+ export const cardFocusClassName = `focus-visible:border-primary focus-visible:outline focus-visible:outline-offset-1
4
4
  focus-visible:outline-on-main`;
@@ -7,7 +7,7 @@ import type { ElementType } from "react";
7
7
  import type { ChipProps } from "./type.js";
8
8
 
9
9
  export const Chip = <T extends ElementType = "div">({ active = false, className, children, ...rest }: ChipProps<T>) => (
10
- <Tag {...Tag.forward<T>(rest)} className={cn(chipBaseClassName, className, active && chipActiveClassName)}>
10
+ <Tag {...Tag.forward<T>(rest)} className={cn(chipBaseClassName, active && chipActiveClassName, className)}>
11
11
  {children}
12
12
  </Tag>
13
13
  );
@@ -6,11 +6,13 @@ import { codeBaseClassName, codeFilledClassName } from "./style.js";
6
6
  import type { ElementType } from "react";
7
7
  import type { CodeProps } from "./type.js";
8
8
 
9
- export const Code = <T extends ElementType = "code">({ filled = false, className, children, ...rest }: CodeProps<T>) => (
10
- <Tag
11
- {...Tag.forward<T>(rest, "code")}
12
- className={cn(codeBaseClassName, filled && codeFilledClassName, className)}
13
- >
9
+ export const Code = <T extends ElementType = "code">({
10
+ filled = false,
11
+ className,
12
+ children,
13
+ ...rest
14
+ }: CodeProps<T>) => (
15
+ <Tag {...Tag.forward<T>(rest, "code")} className={cn(codeBaseClassName, filled && codeFilledClassName, className)}>
14
16
  {children}
15
17
  </Tag>
16
18
  );
@@ -1,12 +1,7 @@
1
1
  import { Tag } from "react-renderable";
2
2
 
3
3
  import { cn } from "../../utils/cn/cn.js";
4
- import {
5
- headerBaseClassName,
6
- headerLeadingClassName,
7
- headerTitleClassName,
8
- headerTrailingClassName,
9
- } from "./style.js";
4
+ import { headerBaseClassName, headerLeadingClassName, headerTitleClassName, headerTrailingClassName } from "./style.js";
10
5
 
11
6
  import type { ElementType } from "react";
12
7
  import type { HeaderLeadingProps, HeaderProps, HeaderTitleProps, HeaderTrailingProps } from "./type.js";
@@ -23,9 +18,9 @@ Header.Leading = ({ children, className }: HeaderLeadingProps) => (
23
18
  </span>
24
19
  );
25
20
 
26
- Header.Title = ({ title, id, className }: HeaderTitleProps) => (
21
+ Header.Title = ({ children, id, className }: HeaderTitleProps) => (
27
22
  <span data-margo-header-slot={true} id={id} className={cn(headerTitleClassName, className)}>
28
- {title}
23
+ {children}
29
24
  </span>
30
25
  );
31
26
 
@@ -9,7 +9,7 @@ export type HeaderLeadingProps = {
9
9
  };
10
10
 
11
11
  export type HeaderTitleProps = {
12
- title: ReactNode;
12
+ children: ReactNode;
13
13
  id?: string;
14
14
  className?: string;
15
15
  };
@@ -22,9 +22,9 @@ export const Input = <T extends ElementType = "div">({
22
22
  />
23
23
  );
24
24
 
25
- Input.Icon = ({ icon = <MdSearch className="text-md" />, className }: InputIconProps) => (
25
+ Input.Icon = ({ children = <MdSearch className="text-md" />, className }: InputIconProps) => (
26
26
  <span data-margo-input-slot={true} className={cn(inputIconClassName, className)}>
27
- {icon}
27
+ {children}
28
28
  </span>
29
29
  );
30
30
 
@@ -9,7 +9,7 @@ export type InputOwnProps = {
9
9
  export type InputProps<T extends ElementType = "div"> = TagProps<T, InputOwnProps>;
10
10
 
11
11
  export type InputIconProps = {
12
- icon?: ReactNode;
12
+ children?: ReactNode;
13
13
  className?: string;
14
14
  };
15
15
 
@@ -20,20 +20,16 @@ export const Item = <T extends ElementType = "button">({
20
20
  clickable = true,
21
21
  disabled = false,
22
22
  active = false,
23
- onClickBlur,
24
23
  onClick,
25
24
  className,
26
25
  children,
27
26
  ...rest
28
27
  }: ItemProps<T>) => {
28
+ const isNativeButton = (rest.as ?? "button") === "button";
29
+
29
30
  const handleClick = (event: MouseEvent<HTMLElement>) => {
30
- if (disabled) return;
31
+ if (disabled) return event.preventDefault();
31
32
  onClick?.(event);
32
- if (typeof onClickBlur === "function") {
33
- const target = event.currentTarget;
34
- onClickBlur(event);
35
- setTimeout(() => target.blur(), 150);
36
- }
37
33
  };
38
34
 
39
35
  return (
@@ -42,8 +38,9 @@ export const Item = <T extends ElementType = "button">({
42
38
  {...Tag.forward<T>(rest, "button")}
43
39
  data-margo-active={active}
44
40
  data-margo-disabled={disabled}
45
- aria-disabled={disabled || undefined}
46
- inert={disabled || !clickable || undefined}
41
+ disabled={(isNativeButton && disabled) || undefined}
42
+ aria-disabled={(!isNativeButton && disabled) || undefined}
43
+ inert={!clickable || undefined}
47
44
  onClick={handleClick}
48
45
  className={cn(
49
46
  itemBaseClassName,
@@ -58,12 +55,12 @@ export const Item = <T extends ElementType = "button">({
58
55
  );
59
56
  };
60
57
 
61
- Item.Icon = ({ icon = <MdChevronRight className="text-md translate-x-1" />, className }: ItemIconProps) => (
58
+ Item.Icon = ({ children = <MdChevronRight className="text-md translate-x-1" />, className }: ItemIconProps) => (
62
59
  <span data-margo-item-slot={true} className={cn(itemIconClassName, className)}>
63
- {icon}
60
+ {children}
64
61
  </span>
65
62
  );
66
63
 
67
- Item.Label = ({ label, className }: ItemLabelProps) => (
68
- <span className={cn(itemLabelClassName, className)}>{label}</span>
64
+ Item.Label = ({ children, className }: ItemLabelProps) => (
65
+ <span className={cn(itemLabelClassName, className)}>{children}</span>
69
66
  );
@@ -8,7 +8,7 @@ hover:shadow-item focus-visible:shadow-item`;
8
8
 
9
9
  export const itemActiveClassName = "border-primary";
10
10
 
11
- export const itemDisabledClassName = "opacity-40";
11
+ export const itemDisabledClassName = "pointer-events-none opacity-40";
12
12
 
13
13
  export const itemIconClassName = `ml-auto flex size-4 shrink-0 items-center justify-center first:ml-0
14
14
  [[data-margo-item-slot]+&]:ml-0`;
@@ -6,17 +6,16 @@ export type ItemOwnProps = {
6
6
  disabled?: boolean;
7
7
  active?: boolean;
8
8
  onClick?: MouseEventHandler<HTMLElement>;
9
- onClickBlur?: MouseEventHandler<HTMLElement>;
10
9
  };
11
10
 
12
11
  export type ItemProps<T extends ElementType = "button"> = TagProps<T, ItemOwnProps>;
13
12
 
14
13
  export type ItemIconProps = {
15
- icon?: ReactNode;
14
+ children?: ReactNode;
16
15
  className?: string;
17
16
  };
18
17
 
19
18
  export type ItemLabelProps = {
20
- label: ReactNode;
19
+ children: ReactNode;
21
20
  className?: string;
22
21
  };
@@ -52,6 +52,20 @@ export const Layer = ({ open = false, onClose, dismissible = true, className, ch
52
52
  };
53
53
  }, [open]);
54
54
 
55
+ useEffect(() => {
56
+ if (!open || dismissible) return;
57
+
58
+ const controller = new AbortController();
59
+
60
+ const handleKeyDown = (event: KeyboardEvent) => {
61
+ if (event.key === "Escape") event.preventDefault();
62
+ };
63
+
64
+ document.addEventListener("keydown", handleKeyDown, { capture: true, signal: controller.signal });
65
+
66
+ return () => controller.abort();
67
+ }, [dismissible, open]);
68
+
55
69
  const handleClick = (event: MouseEvent<HTMLDialogElement>) => {
56
70
  if (!dismissible || event.target !== event.currentTarget) return;
57
71
 
@@ -60,7 +74,7 @@ export const Layer = ({ open = false, onClose, dismissible = true, className, ch
60
74
 
61
75
  const handleCancel = (event: SyntheticEvent<HTMLDialogElement>) => {
62
76
  event.preventDefault();
63
- onClose?.();
77
+ if (dismissible) onClose?.();
64
78
  };
65
79
 
66
80
  return (
@@ -8,7 +8,7 @@ import {
8
8
  popoverBaseClassName,
9
9
  popoverBodyClassName,
10
10
  popoverOriginClassName,
11
- popoverPositionClassName,
11
+ popoverPlacementClassName,
12
12
  } from "./style.js";
13
13
 
14
14
  import type { ElementType } from "react";
@@ -20,7 +20,7 @@ export const Popover = ({
20
20
  open = false,
21
21
  onClose,
22
22
  dismissible = true,
23
- position = "down",
23
+ placement = "bottom",
24
24
  align = "center",
25
25
  className,
26
26
  children,
@@ -90,8 +90,8 @@ export const Popover = ({
90
90
  data-margo-open={open}
91
91
  className={cn(
92
92
  popoverBaseClassName,
93
- popoverPositionClassName[position][align],
94
- popoverOriginClassName[position],
93
+ popoverPlacementClassName[placement][align],
94
+ popoverOriginClassName[placement],
95
95
  className,
96
96
  )}
97
97
  >
@@ -1,4 +1,4 @@
1
- import type { PopoverAlign, PopoverPosition } from "./type.js";
1
+ import type { PopoverAlign, PopoverPlacement } from "./type.js";
2
2
 
3
3
  export const popoverAnchorClassName = "relative inline-block size-fit";
4
4
 
@@ -8,13 +8,13 @@ overflow-clip rounded-margo-base border-margo border-border bg-main text-on-main
8
8
  export const popoverBodyClassName = `flex min-h-0 max-h-56 flex-col gap-1
9
9
  overflow-y-auto overscroll-contain w-64 p-2 text-sm`;
10
10
 
11
- export const popoverPositionClassName: Record<PopoverPosition, Record<PopoverAlign, string>> = {
12
- up: {
11
+ export const popoverPlacementClassName: Record<PopoverPlacement, Record<PopoverAlign, string>> = {
12
+ top: {
13
13
  start: "bottom-full left-0 my-1",
14
14
  center: "bottom-full left-1/2 my-1 -translate-x-1/2",
15
15
  end: "right-0 bottom-full my-1",
16
16
  },
17
- down: {
17
+ bottom: {
18
18
  start: "top-full left-0 my-1",
19
19
  center: "top-full left-1/2 my-1 -translate-x-1/2",
20
20
  end: "top-full right-0 my-1",
@@ -31,9 +31,9 @@ export const popoverPositionClassName: Record<PopoverPosition, Record<PopoverAli
31
31
  },
32
32
  };
33
33
 
34
- export const popoverOriginClassName: Record<PopoverPosition, string> = {
35
- up: "origin-bottom",
36
- down: "origin-top",
34
+ export const popoverOriginClassName: Record<PopoverPlacement, string> = {
35
+ top: "origin-bottom",
36
+ bottom: "origin-top",
37
37
  left: "origin-right",
38
38
  right: "origin-left",
39
39
  };
@@ -1,7 +1,7 @@
1
1
  import type { TagProps } from "react-renderable";
2
2
  import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react";
3
3
 
4
- export type PopoverPosition = "up" | "down" | "left" | "right";
4
+ export type PopoverPlacement = "top" | "right" | "bottom" | "left";
5
5
 
6
6
  export type PopoverAlign = "start" | "center" | "end";
7
7
 
@@ -9,7 +9,7 @@ export type PopoverProps = ComponentPropsWithoutRef<"div"> & {
9
9
  open?: boolean;
10
10
  onClose?: () => void;
11
11
  dismissible?: boolean;
12
- position?: PopoverPosition;
12
+ placement?: PopoverPlacement;
13
13
  align?: PopoverAlign;
14
14
  children?: ReactNode;
15
15
  };
@@ -1,3 +1,4 @@
1
- export const progressBarDefaultFill = "linear-gradient(to right, var(--margo-color-primary-darken), var(--margo-color-primary))";
1
+ export const progressBarDefaultFill =
2
+ "linear-gradient(to right, var(--margo-color-primary-darken), var(--margo-color-primary))";
2
3
 
3
4
  export const progressBarBaseClassName = "margo-progress-bar relative h-0.5 overflow-hidden";
@@ -2,19 +2,19 @@
2
2
  transition: translate var(--margo-layer-duration) var(--margo-layer-easing);
3
3
  }
4
4
 
5
- .margo-sheet[data-margo-side="start"] {
5
+ .margo-sheet[data-margo-placement="left"] {
6
6
  translate: -100% 0;
7
7
  }
8
8
 
9
- .margo-sheet[data-margo-side="end"] {
9
+ .margo-sheet[data-margo-placement="right"] {
10
10
  translate: 100% 0;
11
11
  }
12
12
 
13
- .margo-sheet[data-margo-side="top"] {
13
+ .margo-sheet[data-margo-placement="top"] {
14
14
  translate: 0 -100%;
15
15
  }
16
16
 
17
- .margo-sheet[data-margo-side="bottom"] {
17
+ .margo-sheet[data-margo-placement="bottom"] {
18
18
  translate: 0 100%;
19
19
  }
20
20
 
@@ -23,19 +23,19 @@ dialog[open] .margo-sheet {
23
23
  }
24
24
 
25
25
  @starting-style {
26
- dialog[open] .margo-sheet[data-margo-side="start"] {
26
+ dialog[open] .margo-sheet[data-margo-placement="left"] {
27
27
  translate: -100% 0;
28
28
  }
29
29
 
30
- dialog[open] .margo-sheet[data-margo-side="end"] {
30
+ dialog[open] .margo-sheet[data-margo-placement="right"] {
31
31
  translate: 100% 0;
32
32
  }
33
33
 
34
- dialog[open] .margo-sheet[data-margo-side="top"] {
34
+ dialog[open] .margo-sheet[data-margo-placement="top"] {
35
35
  translate: 0 -100%;
36
36
  }
37
37
 
38
- dialog[open] .margo-sheet[data-margo-side="bottom"] {
38
+ dialog[open] .margo-sheet[data-margo-placement="bottom"] {
39
39
  translate: 0 100%;
40
40
  }
41
41
  }
@@ -1,19 +1,24 @@
1
1
  import { Tag } from "react-renderable";
2
2
 
3
3
  import { cn } from "../../utils/cn/cn.js";
4
- import { sheetBaseClassName, sheetBodyClassName, sheetFooterClassName, sheetSideClassName } from "./style.js";
4
+ import { sheetBaseClassName, sheetBodyClassName, sheetFooterClassName, sheetPlacementClassName } from "./style.js";
5
5
 
6
6
  import type { ElementType } from "react";
7
7
  import type { SheetBodyProps, SheetFooterProps, SheetProps } from "./type.js";
8
8
 
9
9
  import "./sheet.css";
10
10
 
11
- export const Sheet = <T extends ElementType = "div">({ side = "end", className, children, ...rest }: SheetProps<T>) => (
11
+ export const Sheet = <T extends ElementType = "div">({
12
+ placement = "right",
13
+ className,
14
+ children,
15
+ ...rest
16
+ }: SheetProps<T>) => (
12
17
  <Tag
13
18
  {...Tag.forward<T>(rest)}
14
- data-margo-side={side}
19
+ data-margo-placement={placement}
15
20
  data-margo-travel={true}
16
- className={cn(sheetBaseClassName, sheetSideClassName[side], className)}
21
+ className={cn(sheetBaseClassName, sheetPlacementClassName[placement], className)}
17
22
  >
18
23
  {children}
19
24
  </Tag>
@@ -1,10 +1,10 @@
1
- import type { SheetSide } from "./type.js";
1
+ import type { SheetPlacement } from "./type.js";
2
2
 
3
3
  export const sheetBaseClassName = "margo-sheet flex flex-col overflow-hidden border-border bg-main text-on-main";
4
4
 
5
- export const sheetSideClassName: Record<SheetSide, string> = {
6
- start: "h-full w-[85%] max-w-[26rem] justify-self-start self-stretch border-r-margo",
7
- end: "h-full w-[85%] max-w-[26rem] justify-self-end self-stretch border-l-margo",
5
+ export const sheetPlacementClassName: Record<SheetPlacement, string> = {
6
+ left: "h-full w-[85%] max-w-[26rem] justify-self-start self-stretch border-r-margo",
7
+ right: "h-full w-[85%] max-w-[26rem] justify-self-end self-stretch border-l-margo",
8
8
  top: "h-auto max-h-[40%] w-full self-start justify-self-stretch border-b-margo",
9
9
  bottom: "h-auto max-h-[40%] w-full self-end justify-self-stretch border-t-margo",
10
10
  };
@@ -1,10 +1,10 @@
1
1
  import type { TagProps } from "react-renderable";
2
2
  import type { ElementType } from "react";
3
3
 
4
- export type SheetSide = "start" | "end" | "top" | "bottom";
4
+ export type SheetPlacement = "top" | "right" | "bottom" | "left";
5
5
 
6
6
  export type SheetOwnProps = {
7
- side?: SheetSide;
7
+ placement?: SheetPlacement;
8
8
  };
9
9
 
10
10
  export type SheetProps<T extends ElementType = "div"> = TagProps<T, SheetOwnProps>;
@@ -1,4 +1,5 @@
1
- export const tableWrapperClassName = "w-full overflow-x-auto overscroll-x-none rounded-margo-base border-margo border-border";
1
+ export const tableWrapperClassName =
2
+ "w-full overflow-x-auto overscroll-x-none rounded-margo-base border-margo border-border";
2
3
 
3
4
  export const tableBaseClassName = "w-full border-collapse text-xs text-on-main";
4
5
 
@@ -0,0 +1,10 @@
1
+ export const textAreaBaseClassName = `group/text-area flex w-full cursor-text items-stretch rounded-margo-base
2
+ border-margo border-transparent bg-low text-sm text-medium
3
+ hover:border-on-main hover:text-on-main hover:shadow-item
4
+ focus-within:border-primary focus-within:text-on-main
5
+ has-[textarea:disabled]:pointer-events-none has-[textarea:disabled]:cursor-auto has-[textarea:disabled]:opacity-40`;
6
+
7
+ export const textAreaActiveClassName = "border-primary";
8
+
9
+ export const textAreaTextClassName = `min-w-0 flex-1 resize-none border-none bg-transparent px-3 py-2 text-left
10
+ text-on-main outline-none placeholder:text-medium disabled:cursor-auto`;
@@ -0,0 +1,25 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { textAreaActiveClassName, textAreaBaseClassName, textAreaTextClassName } from "./style.js";
5
+
6
+ import type { ElementType } from "react";
7
+ import type { TextAreaProps, TextAreaTextProps } from "./type.js";
8
+
9
+ export const TextArea = <T extends ElementType = "div">({
10
+ active = false,
11
+ clickable = true,
12
+ className,
13
+ ...rest
14
+ }: TextAreaProps<T>) => (
15
+ <Tag
16
+ {...Tag.forward<T>(rest, "div")}
17
+ data-margo-active={active}
18
+ inert={!clickable || undefined}
19
+ className={cn(textAreaBaseClassName, active && textAreaActiveClassName, className)}
20
+ />
21
+ );
22
+
23
+ TextArea.Text = ({ rows = 4, className, ...rest }: TextAreaTextProps) => (
24
+ <textarea {...rest} rows={rows} className={cn(textAreaTextClassName, className)} />
25
+ );
@@ -0,0 +1,11 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ComponentPropsWithoutRef, ElementType } from "react";
3
+
4
+ export type TextAreaOwnProps = {
5
+ active?: boolean;
6
+ clickable?: boolean;
7
+ };
8
+
9
+ export type TextAreaProps<T extends ElementType = "div"> = TagProps<T, TextAreaOwnProps>;
10
+
11
+ export type TextAreaTextProps = ComponentPropsWithoutRef<"textarea">;
@@ -1,17 +1,17 @@
1
- import type { TooltipPosition } from "./type.js";
1
+ import type { TooltipPlacement } from "./type.js";
2
2
 
3
- export const tooltipAnchorClassName = "group relative inline-block size-fit";
3
+ export const tooltipAnchorClassName = "group/tooltip relative inline-block size-fit";
4
4
 
5
5
  export const tooltipBubbleClassName = `pointer-events-none absolute z-10 rounded-margo-xs bg-on-main px-2 py-0.5 text-xs text-main lowercase
6
6
  whitespace-nowrap opacity-0`;
7
7
 
8
8
  export const tooltipTransitionClassName = "transition-opacity duration-[250ms] ease-in-out";
9
9
 
10
- export const tooltipVisibleClassName = "group-hover:opacity-100 group-focus-within:opacity-100";
10
+ export const tooltipVisibleClassName = "group-hover/tooltip:opacity-100 group-focus-within/tooltip:opacity-100";
11
11
 
12
- export const tooltipPositionClassName: Record<TooltipPosition, string> = {
13
- up: "bottom-[120%] left-1/2 -translate-x-1/2",
14
- down: "top-[120%] left-1/2 -translate-x-1/2",
12
+ export const tooltipPlacementClassName: Record<TooltipPlacement, string> = {
13
+ top: "bottom-[120%] left-1/2 -translate-x-1/2",
14
+ bottom: "top-[120%] left-1/2 -translate-x-1/2",
15
15
  left: "top-1/2 right-[calc(100%+0.5rem)] -translate-y-1/2",
16
16
  right: "top-1/2 left-[calc(100%+0.5rem)] -translate-y-1/2",
17
17
  };
@@ -6,7 +6,7 @@ import { cn } from "../../utils/cn/cn.js";
6
6
  import {
7
7
  tooltipAnchorClassName,
8
8
  tooltipBubbleClassName,
9
- tooltipPositionClassName,
9
+ tooltipPlacementClassName,
10
10
  tooltipTransitionClassName,
11
11
  tooltipVisibleClassName,
12
12
  } from "./style.js";
@@ -20,7 +20,7 @@ export const Tooltip = <T extends ElementType = "div">({
20
20
  className,
21
21
  id,
22
22
  content,
23
- position = "down",
23
+ placement = "bottom",
24
24
  ...rest
25
25
  }: TooltipProps<T>) => {
26
26
  const contentRef = useRef<ReactNode>(null);
@@ -40,7 +40,7 @@ export const Tooltip = <T extends ElementType = "div">({
40
40
  tooltipBubbleClassName,
41
41
  tooltipTransitionClassName,
42
42
  isFilled && tooltipVisibleClassName,
43
- tooltipPositionClassName[position],
43
+ tooltipPlacementClassName[placement],
44
44
  )}
45
45
  >
46
46
  {contentRef.current}
@@ -1,13 +1,13 @@
1
1
  import type { TagProps } from "react-renderable";
2
2
  import type { ElementType, ReactNode } from "react";
3
3
 
4
- export type TooltipPosition = "up" | "down" | "left" | "right";
4
+ export type TooltipPlacement = "top" | "right" | "bottom" | "left";
5
5
 
6
6
  export type TooltipOwnProps = {
7
7
  label?: string;
8
8
  id?: string;
9
9
  content?: ReactNode;
10
- position?: TooltipPosition;
10
+ placement?: TooltipPlacement;
11
11
  children?: ReactNode;
12
12
  };
13
13
 
package/src/css/base.css CHANGED
@@ -5,10 +5,6 @@
5
5
  box-sizing: border-box;
6
6
  }
7
7
 
8
- canvas {
9
- width: 100% !important;
10
- }
11
-
12
8
  li {
13
9
  list-style: none;
14
10
  }
@@ -22,16 +18,6 @@
22
18
  text-align: left;
23
19
  }
24
20
 
25
- ::-webkit-scrollbar {
26
- display: none;
27
- -webkit-appearance: none;
28
- }
29
-
30
- ::-webkit-scrollbar-button {
31
- display: none;
32
- -webkit-appearance: none;
33
- }
34
-
35
21
  input::-webkit-outer-spin-button,
36
22
  input::-webkit-inner-spin-button {
37
23
  -webkit-appearance: none;
package/src/css/theme.css CHANGED
@@ -31,8 +31,7 @@
31
31
  --shadow-item: var(--margo-shadow-item);
32
32
  --shadow-chip: var(--margo-shadow-chip);
33
33
 
34
- /* Durations */
35
- --duration-layer: var(--margo-layer-duration);
34
+ /* Easings */
36
35
  --ease-layer: var(--margo-layer-easing);
37
36
 
38
37
  /* Layers */
@@ -7,7 +7,8 @@
7
7
  opacity: 0;
8
8
  border-radius: inherit;
9
9
  background: radial-gradient(
10
- var(--margo-background-glow-size, 480px) circle at var(--margo-background-glow-x, 50%) var(--margo-background-glow-y, 50%),
10
+ var(--margo-background-glow-size, 480px) circle at var(--margo-background-glow-x, 50%)
11
+ var(--margo-background-glow-y, 50%),
11
12
  var(--color-primary),
12
13
  transparent 70%
13
14
  );
@@ -30,13 +31,10 @@
30
31
 
31
32
  @media (hover: hover) and (pointer: fine) {
32
33
  .margo-background-glow:hover::before {
33
- opacity: var(--margo-background-glow-opacity, 0.1);
34
+ opacity: var(--margo-background-glow-opacity, 0.2);
34
35
  }
35
36
 
36
37
  .margo-background-glow:hover::after {
37
38
  opacity: var(--margo-background-glow-noise-opacity, 0.04);
38
39
  }
39
40
  }
40
-
41
-
42
-
@@ -1,14 +1,14 @@
1
1
  import { cloneElement } from "react";
2
2
 
3
3
  import { cn } from "../../utils/cn/cn.js";
4
- import { borderGlowBaseClassName, borderGlowPositionClassName } from "./style.js";
4
+ import { borderGlowBaseClassName, borderGlowPlacementClassName } from "./style.js";
5
5
 
6
6
  import type { PointerEvent } from "react";
7
7
  import type { BorderGlowProps } from "./type.js";
8
8
 
9
9
  import "./border_glow.css";
10
10
 
11
- export const BorderGlow = ({ children, position = "all", tolerance = 1 }: BorderGlowProps) => {
11
+ export const BorderGlow = ({ children, placement = "all", tolerance = 1 }: BorderGlowProps) => {
12
12
  const handlePointerMove = (event: PointerEvent<HTMLElement>) => {
13
13
  if (event.pointerType !== "mouse") return children.props.onPointerMove?.(event);
14
14
 
@@ -18,14 +18,17 @@ export const BorderGlow = ({ children, position = "all", tolerance = 1 }: Border
18
18
  const size = Math.max(bounds.width, bounds.height) * Math.min(Math.max(tolerance, 0), 1);
19
19
 
20
20
  event.currentTarget.style.setProperty("--margo-border-glow-size", `${size}px`);
21
- event.currentTarget.style.setProperty("--margo-border-glow-x", `${position === "right" ? bounds.width - x : x}px`);
22
- event.currentTarget.style.setProperty("--margo-border-glow-y", `${position === "down" ? bounds.height - y : y}px`);
21
+ event.currentTarget.style.setProperty("--margo-border-glow-x", `${placement === "right" ? bounds.width - x : x}px`);
22
+ event.currentTarget.style.setProperty(
23
+ "--margo-border-glow-y",
24
+ `${placement === "bottom" ? bounds.height - y : y}px`,
25
+ );
23
26
 
24
27
  children.props.onPointerMove?.(event);
25
28
  };
26
29
 
27
30
  return cloneElement(children, {
28
- className: cn(borderGlowBaseClassName, borderGlowPositionClassName[position], children.props.className),
31
+ className: cn(borderGlowBaseClassName, borderGlowPlacementClassName[placement], children.props.className),
29
32
  onPointerMove: handlePointerMove,
30
33
  });
31
34
  };
@@ -1,10 +1,10 @@
1
- import type { BorderGlowPosition } from "./type.js";
1
+ import type { BorderGlowPlacement } from "./type.js";
2
2
 
3
3
  export const borderGlowBaseClassName = "relative isolate margo-border-glow";
4
4
 
5
- export const borderGlowPositionClassName: Record<BorderGlowPosition, string> = {
6
- up: "margo-border-glow--edge-top",
7
- down: "margo-border-glow--edge-bottom",
5
+ export const borderGlowPlacementClassName: Record<BorderGlowPlacement, string> = {
6
+ top: "margo-border-glow--edge-top",
7
+ bottom: "margo-border-glow--edge-bottom",
8
8
  left: "margo-border-glow--edge-left",
9
9
  right: "margo-border-glow--edge-right",
10
10
  all: "margo-border-glow--ring",
@@ -1,6 +1,6 @@
1
1
  import type { PointerEventHandler, ReactElement } from "react";
2
2
 
3
- export type BorderGlowPosition = "up" | "down" | "left" | "right" | "all";
3
+ export type BorderGlowPlacement = "top" | "right" | "bottom" | "left" | "all";
4
4
 
5
5
  export type BorderGlowChildProps = {
6
6
  className?: string;
@@ -9,6 +9,6 @@ export type BorderGlowChildProps = {
9
9
 
10
10
  export type BorderGlowProps = {
11
11
  children: ReactElement<BorderGlowChildProps>;
12
- position?: BorderGlowPosition;
12
+ placement?: BorderGlowPlacement;
13
13
  tolerance?: number;
14
14
  };
@@ -9,7 +9,11 @@ const readKey = (target: EventTarget | null, attribute: string) => {
9
9
  return holder?.getAttribute(attribute) ?? null;
10
10
  };
11
11
 
12
- export const PointerHolder = ({ children, attribute = "data-margo-pointer-holder", disabled = false }: PointerHolderProps) => {
12
+ export const PointerHolder = ({
13
+ children,
14
+ attribute = "data-margo-pointer-holder",
15
+ disabled = false,
16
+ }: PointerHolderProps) => {
13
17
  const [keys, setKeys] = useState<string[]>([]);
14
18
 
15
19
  const hold = (target: EventTarget | null) => {
@@ -1,12 +1,10 @@
1
- import type { FocusEventHandler, PointerEventHandler, ReactElement } from "react";
1
+ import type { PointerEventHandler, ReactElement } from "react";
2
2
 
3
3
  export type PointerHolderApi = {
4
4
  keys: string[];
5
5
  };
6
6
 
7
7
  export type PointerHolderChildProps = {
8
- onBlur?: FocusEventHandler<HTMLElement>;
9
- onFocus?: FocusEventHandler<HTMLElement>;
10
8
  onPointerLeave?: PointerEventHandler<HTMLElement>;
11
9
  onPointerOver?: PointerEventHandler<HTMLElement>;
12
10
  };
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Blockquote } from "./components/blockquote/blockquote.js";
2
2
  export { Button } from "./components/button/button.js";
3
3
  export { ButtonMicro } from "./components/button_micro/button_micro.js";
4
+ export { Callout } from "./components/callout/callout.js";
4
5
  export { Card } from "./components/card/card.js";
5
6
  export { Checkbox } from "./components/checkbox/checkbox.js";
6
7
  export { Chip } from "./components/chip/chip.js";
@@ -17,6 +18,7 @@ export { Table } from "./components/table/table.js";
17
18
  export { Toggle } from "./components/toggle/toggle.js";
18
19
  export { Select } from "./components/select/select.js";
19
20
  export { Sheet } from "./components/sheet/sheet.js";
21
+ export { TextArea } from "./components/text_area/text_area.js";
20
22
  export { Spinner } from "./components/spinner/spinner.js";
21
23
  export { Tooltip } from "./components/tooltip/tooltip.js";
22
24
 
@@ -39,12 +41,13 @@ export type { BlockquoteProps } from "./components/blockquote/type.js";
39
41
 
40
42
  export type {
41
43
  ButtonIconLabelProps,
42
- ButtonIconLabelSide,
44
+ ButtonIconLabelDirection,
43
45
  ButtonIconProps,
44
46
  ButtonLabelProps,
45
47
  ButtonProps,
46
48
  } from "./components/button/type.js";
47
49
  export type { ButtonMicroProps } from "./components/button_micro/type.js";
50
+ export type { CalloutProps, CalloutTitleProps } from "./components/callout/type.js";
48
51
  export type { CardProps } from "./components/card/type.js";
49
52
  export type { CheckboxProps } from "./components/checkbox/type.js";
50
53
  export type { ChipProps } from "./components/chip/type.js";
@@ -60,7 +63,13 @@ export type { LabelOwnProps, LabelProps } from "./components/label/type.js";
60
63
  export type { LayerProps } from "./components/layer/type.js";
61
64
  export type { InputIconProps, InputProps, InputTextProps } from "./components/input/type.js";
62
65
  export type { ItemIconProps, ItemLabelProps, ItemProps } from "./components/item/type.js";
63
- export type { PopoverAlign, PopoverAnchorProps, PopoverBodyProps, PopoverPosition, PopoverProps } from "./components/popover/type.js";
66
+ export type {
67
+ PopoverAlign,
68
+ PopoverAnchorProps,
69
+ PopoverBodyProps,
70
+ PopoverPlacement,
71
+ PopoverProps,
72
+ } from "./components/popover/type.js";
64
73
  export type { ProgressBarMode, ProgressBarProps } from "./components/progress_bar/type.js";
65
74
  export type {
66
75
  TableBodyProps,
@@ -71,13 +80,14 @@ export type {
71
80
  TableRowProps,
72
81
  } from "./components/table/type.js";
73
82
  export type { SelectOwnProps, SelectProps } from "./components/select/type.js";
74
- export type { SheetBodyProps, SheetFooterProps, SheetProps, SheetSide } from "./components/sheet/type.js";
83
+ export type { SheetBodyProps, SheetFooterProps, SheetPlacement, SheetProps } from "./components/sheet/type.js";
75
84
  export type { SpinnerProps } from "./components/spinner/type.js";
85
+ export type { TextAreaOwnProps, TextAreaProps, TextAreaTextProps } from "./components/text_area/type.js";
76
86
  export type { ToggleProps } from "./components/toggle/type.js";
77
- export type { TooltipPosition, TooltipProps } from "./components/tooltip/type.js";
87
+ export type { TooltipPlacement, TooltipProps } from "./components/tooltip/type.js";
78
88
 
79
89
  export type { BackgroundGlowProps } from "./hoc/background_glow/type.js";
80
- export type { BorderGlowPosition, BorderGlowProps } from "./hoc/border_glow/type.js";
90
+ export type { BorderGlowPlacement, BorderGlowProps } from "./hoc/border_glow/type.js";
81
91
  export type { PointerHolderProps } from "./hoc/pointer_holder/type.js";
82
92
  export type { MaskGradientXProps } from "./hoc/mask_gradient_x/type.js";
83
93
  export type { MaskGradientYProps } from "./hoc/mask_gradient_y/type.js";
@@ -9,6 +9,9 @@ const isColumnLine = (value: string) => value === "full" || value === "content"
9
9
 
10
10
  const isMargoRadiusStep = (value: string) => /^margo-(mc|xs|sm|base)$/.test(value);
11
11
 
12
+ const MARGO_LEADING_STEPS = ["mc", "xs", "sm", "md", "lg", "base", "xl", "2xl"];
13
+ const MARGO_SHADOWS = ["card", "button", "item", "chip"];
14
+
12
15
  const BORDER_SIDES = ["", "x", "y", "s", "e", "t", "r", "b", "l"] as const;
13
16
  const RADIUS_CORNERS = ["", "s", "e", "t", "r", "b", "l", "ss", "se", "ee", "es", "tl", "tr", "br", "bl"] as const;
14
17
 
@@ -40,9 +43,16 @@ type MargoClassGroupIds =
40
43
  | "margo-padding";
41
44
 
42
45
  const twMerge = extendTailwindMerge<MargoClassGroupIds>({
46
+ override: {
47
+ conflictingClassGroups: {
48
+ "font-size": [],
49
+ },
50
+ },
43
51
  extend: {
44
52
  classGroups: {
45
53
  "font-size": ["text-mc"],
54
+ leading: [{ leading: MARGO_LEADING_STEPS }],
55
+ shadow: [{ shadow: MARGO_SHADOWS }],
46
56
  ...borderWidthGroups,
47
57
  ...radiusGroups,
48
58
  "margo-col": [{ "margo-col": [isColumnLine] }],