margo-ui 1.3.3 → 1.3.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "margo-ui",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "A small React UI kit built on Tailwind CSS v4 design tokens, themeable through plain CSS custom properties.",
5
5
  "keywords": [
6
6
  "react",
@@ -17,6 +17,7 @@ import {
17
17
  } from "./style.js";
18
18
 
19
19
  import type { ElementType } from "react";
20
+ import { MaskGradientX } from "../../hoc/mask_gradient_x/mask_gradient_x.js";
20
21
  import type { SnippetProps } from "./type.js";
21
22
 
22
23
  export const Snippet = <T extends ElementType = "div">({ snippet, title, className, ...rest }: SnippetProps<T>) => {
@@ -46,18 +47,20 @@ export const Snippet = <T extends ElementType = "div">({ snippet, title, classNa
46
47
  />
47
48
  </Button>
48
49
  </div>
49
- <pre className={snippetCodeClassName}>
50
- <code>
51
- <List
52
- array={snippetHighlight(source)}
53
- itemExtractor={({ row, index }) => (
54
- <span key={index} className={snippetTokenClassName[row.type]}>
55
- {row.value}
56
- </span>
57
- )}
58
- />
59
- </code>
60
- </pre>
50
+ <MaskGradientX fade="1rem">
51
+ <pre className={snippetCodeClassName}>
52
+ <code>
53
+ <List
54
+ array={snippetHighlight(source)}
55
+ itemExtractor={({ row, index }) => (
56
+ <span key={index} className={snippetTokenClassName[row.type]}>
57
+ {row.value}
58
+ </span>
59
+ )}
60
+ />
61
+ </code>
62
+ </pre>
63
+ </MaskGradientX>
61
64
  </Tag>
62
65
  );
63
66
  };
package/src/css/grid.css CHANGED
@@ -63,6 +63,24 @@
63
63
  row-gap: var(--margo-grid-gutter-y);
64
64
  }
65
65
 
66
+ /*
67
+ * Row subgrid: the item spans N rows of its parent grid and shares those tracks
68
+ * with its siblings, so sibling boxes with different content lengths keep their
69
+ * internal sections — dividers, footers, actions — on the exact same lines.
70
+ * Must be applied to every sibling that has to align, all with the same N, and
71
+ * each must expose exactly N children (wrap what belongs together).
72
+ *
73
+ * The rows inherit the parent row-gap; override it on the item (same value on
74
+ * every sibling) when the internal spacing differs from the layout gutter.
75
+ *
76
+ * Use on: cards in a row whose separators must line up.
77
+ */
78
+ @utility margo-grid-sub-rows-* {
79
+ display: grid;
80
+ grid-template-rows: subgrid;
81
+ grid-row-end: span --value(integer);
82
+ }
83
+
66
84
  /*
67
85
  * Standalone 12-column grid: same line names and gutters, but no max-width and
68
86
  * no side padding — it fills whatever box it is placed in.
@@ -0,0 +1,40 @@
1
+ import { cloneElement, useState } from "react";
2
+
3
+ import type { PointerEvent } from "react";
4
+ import type { PointerHolderProps } from "./type.js";
5
+
6
+ const readKey = (target: EventTarget | null, attribute: string) => {
7
+ if (!(target instanceof Element)) return null;
8
+ const holder = target.closest(`[${attribute}]`);
9
+ return holder?.getAttribute(attribute) ?? null;
10
+ };
11
+
12
+ export const PointerHolder = ({ children, attribute = "data-margo-pointer-holder", disabled = false }: PointerHolderProps) => {
13
+ const [keys, setKeys] = useState<string[]>([]);
14
+
15
+ const hold = (target: EventTarget | null) => {
16
+ const key = readKey(target, attribute);
17
+
18
+ if (key === null) return;
19
+ setKeys((prev) => (prev.includes(key) ? prev : [...prev, key]));
20
+ };
21
+
22
+ const release = () => setKeys((prev) => (prev.length ? [] : prev));
23
+
24
+ const child = children({ keys });
25
+
26
+ const handlePointerOver = (event: PointerEvent<HTMLElement>) => {
27
+ if (!disabled && event.pointerType !== "touch") hold(event.target);
28
+ child.props.onPointerOver?.(event);
29
+ };
30
+
31
+ const handlePointerLeave = (event: PointerEvent<HTMLElement>) => {
32
+ release();
33
+ child.props.onPointerLeave?.(event);
34
+ };
35
+
36
+ return cloneElement(child, {
37
+ onPointerLeave: handlePointerLeave,
38
+ onPointerOver: handlePointerOver,
39
+ });
40
+ };
@@ -0,0 +1,18 @@
1
+ import type { FocusEventHandler, PointerEventHandler, ReactElement } from "react";
2
+
3
+ export type PointerHolderApi = {
4
+ keys: string[];
5
+ };
6
+
7
+ export type PointerHolderChildProps = {
8
+ onBlur?: FocusEventHandler<HTMLElement>;
9
+ onFocus?: FocusEventHandler<HTMLElement>;
10
+ onPointerLeave?: PointerEventHandler<HTMLElement>;
11
+ onPointerOver?: PointerEventHandler<HTMLElement>;
12
+ };
13
+
14
+ export type PointerHolderProps = {
15
+ children: (api: PointerHolderApi) => ReactElement<PointerHolderChildProps>;
16
+ attribute?: string;
17
+ disabled?: boolean;
18
+ };
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export { Tooltip } from "./components/tooltip/tooltip.js";
18
18
 
19
19
  export { BackgroundGlow } from "./hoc/background_glow/background_glow.js";
20
20
  export { BorderGlow } from "./hoc/border_glow/border_glow.js";
21
+ export { PointerHolder } from "./hoc/pointer_holder/pointer_holder.js";
21
22
  export { MaskGradientX } from "./hoc/mask_gradient_x/mask_gradient_x.js";
22
23
  export { MaskGradientY } from "./hoc/mask_gradient_y/mask_gradient_y.js";
23
24
  export { Ripple } from "./hoc/ripple/ripple.js";
@@ -68,6 +69,7 @@ export type { TooltipPosition, TooltipProps } from "./components/tooltip/type.js
68
69
 
69
70
  export type { BackgroundGlowProps } from "./hoc/background_glow/type.js";
70
71
  export type { BorderGlowPosition, BorderGlowProps } from "./hoc/border_glow/type.js";
72
+ export type { PointerHolderProps } from "./hoc/pointer_holder/type.js";
71
73
  export type { MaskGradientXProps } from "./hoc/mask_gradient_x/type.js";
72
74
  export type { MaskGradientYProps } from "./hoc/mask_gradient_y/type.js";
73
75
  export type { RippleProps } from "./hoc/ripple/type.js";