@mt-gloss/ui 0.1.6 → 0.1.8

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.
@@ -0,0 +1,55 @@
1
+ import { MotionValue } from 'framer-motion';
2
+ import * as React from 'react';
3
+ /**
4
+ * Per-card MotionValue pair carried alongside its cardId. Consumers (Plan 05
5
+ * integration) derive this from `useDragFeedback.cascadePreview.motionValues`
6
+ * via `Array.from(motionValues.entries(), ([cardId, { x, y }]) => ({ cardId, x, y }))`.
7
+ *
8
+ * **Scalar x/y (NOT a tuple MV)** — framer-motion's `style.x` and `style.y`
9
+ * consume scalar MotionValue<number>. Matches the per-card per-axis precedent
10
+ * in `useEdgeHoverResize.ts:495-547` (ghostWidth + spring-back-on-cancel).
11
+ */
12
+ export interface CascadePreviewItem {
13
+ cardId: string;
14
+ x: MotionValue<number>;
15
+ y: MotionValue<number>;
16
+ }
17
+ export interface CascadePreviewProps {
18
+ items: CascadePreviewItem[];
19
+ /** Render-prop children — called once per item. Consumer owns the per-card
20
+ * display (typically a clone of the displaced source card). */
21
+ children: (item: CascadePreviewItem) => React.ReactNode;
22
+ className?: string;
23
+ }
24
+ /**
25
+ * Phase 3 DRAG-07/08/09 — per-card cascade-preview render primitive.
26
+ *
27
+ * Renders N `<motion.div>` siblings keyed by `item.cardId` (DRAG-09 stable
28
+ * keys — same cardId across re-renders retains the same DOM node so the
29
+ * consumer's MotionValue drives transform without a remount).
30
+ *
31
+ * Each child:
32
+ * - `key={item.cardId}` — React reconciliation stability (DRAG-09)
33
+ * - `data-card-id={item.cardId}` — test hook + attribute-selector targeting
34
+ * - `style.x = item.x`, `style.y = item.y` — framer-motion reads MVs directly
35
+ * - `aria-hidden="true"` — displaced cards are visually cloned during preview;
36
+ * the semantic source card retains focus/aria surface
37
+ * - `pointer-events: none` + `will-change: transform` via SCSS
38
+ *
39
+ * Returns `null` when `items.length === 0` so the consumer does not render an
40
+ * empty fragment wrapper.
41
+ *
42
+ * **MotionValue ownership**: CascadePreview does NOT instantiate MotionValues.
43
+ * The consumer (Plan 05 inside MetricCardGrid) owns the per-instance
44
+ * `useRef<Map<cardId, {x, y}>>` reconciliation (see useDragFeedback.ts
45
+ * `reconcileMotionValues`). Module-scope MVs would cross-contaminate between
46
+ * grid instances; this primitive is purely presentational. See
47
+ * `useRubberBandGhost.ts:57-61` precedent.
48
+ *
49
+ * **Rendering-layer invariants**:
50
+ * - Rendered inside the grid container (NOT inside `<DragOverlay>`); DragOverlay
51
+ * hosts the DragGhost only.
52
+ * - Consumer places this inside a `position: relative` parent so `position:
53
+ * absolute` items stack correctly.
54
+ */
55
+ export declare function CascadePreview({ items, children, className, }: CascadePreviewProps): JSX.Element | null;
@@ -0,0 +1,2 @@
1
+ export { CascadePreview } from './CascadePreview';
2
+ export type { CascadePreviewProps, CascadePreviewItem } from './CascadePreview';
@@ -0,0 +1,35 @@
1
+ import { MotionValue } from 'framer-motion';
2
+ import * as React from 'react';
3
+ export interface DragGhostProps {
4
+ /** 'ok' = blue dashed (--gloss-valid-stroke/fill); 'rejected' = pink solid (--gloss-rejection-stroke/fill) */
5
+ tint: 'ok' | 'rejected';
6
+ colSpan: 1 | 2 | 3;
7
+ colWidth: number;
8
+ rowHeight: number;
9
+ gap: number;
10
+ /** From parent useSpring(cursorMV, cascadePhysics.spring) — produces the 60ms ease-out lag */
11
+ x: MotionValue<number>;
12
+ y: MotionValue<number>;
13
+ /** Dragged card render (passed from consumer) */
14
+ children?: React.ReactNode;
15
+ className?: string;
16
+ }
17
+ /**
18
+ * Phase 3 DRAG-01/02 — presentational ghost card rendered inside dnd-kit `<DragOverlay>`.
19
+ *
20
+ * Spring-driven x/y MotionValue props produce the 60ms ease-out cursor lag
21
+ * (per Phase 0 `cascadePhysics.spring` {stiffness:380, damping:32}).
22
+ *
23
+ * `data-tint` flips between 'ok' (dashed blue) and 'rejected' (solid pink) based on
24
+ * `predictCascade()` result emitted by `useDragFeedback.ghost.tint`. Reduced-motion
25
+ * CSS override strips the 1.02 scale + elevated shadow + transitions.
26
+ *
27
+ * **Consumer invariants (Plan 04 MUST honor — from Plan 00 spike + Plan 01 JSDoc):**
28
+ * 1. Render inside `<DragOverlay dropAnimation={null}>`
29
+ * 2. `x` / `y` are per-instance `useRef<MotionValue<number>>` (NEVER module-scope)
30
+ * 3. Parent wraps raw cursor MotionValues via `useSpring(cursor, cascadePhysics.spring)`
31
+ * — DragGhost itself is purely presentational and never instantiates springs.
32
+ *
33
+ * `aria-hidden="true"` because the dragged card semantics belong to the source, not the ghost.
34
+ */
35
+ export declare function DragGhost({ tint, colSpan, colWidth, rowHeight, gap, x, y, children, className, }: DragGhostProps): JSX.Element;
@@ -0,0 +1,2 @@
1
+ export { DragGhost } from './DragGhost';
2
+ export type { DragGhostProps } from './DragGhost';
@@ -6,6 +6,14 @@ export interface DropAnchorCellProps extends React.HTMLAttributes<HTMLDivElement
6
6
  canAccept: boolean;
7
7
  /** Brief not-allowed pulse when user attempts drop on occupied. */
8
8
  blocked?: boolean;
9
+ /**
10
+ * DRAG-05 — cursor-driven continuous opacity. When provided, the cell
11
+ * renders with `data-proximity-driven='true'` and an inline CSS var
12
+ * `--_proximity-opacity: {value}` that overrides the discrete `data-density`
13
+ * bucket opacity. Phase 23 callers that only pass `density` + `canAccept`
14
+ * see zero behavior change. `canAccept='false'` still suppresses opacity.
15
+ */
16
+ proximityOpacity?: number;
9
17
  /** Stable cell id for test selectors + future instrumentation. */
10
18
  ['data-cell-id']?: string;
11
19
  children?: React.ReactNode;
@@ -26,4 +34,4 @@ export interface DropAnchorCellProps extends React.HTMLAttributes<HTMLDivElement
26
34
  *
27
35
  * Respects prefers-reduced-motion.
28
36
  */
29
- export declare function DropAnchorCell({ density, canAccept, blocked, className, children, ...rest }: DropAnchorCellProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function DropAnchorCell({ density, canAccept, blocked, proximityOpacity, className, children, ...rest }: DropAnchorCellProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,39 @@
1
+ export interface SnapOutlineProps {
2
+ cell: {
3
+ section: 0 | 1 | 2 | 3;
4
+ col: 0 | 1 | 2;
5
+ row: 0 | 1 | 2;
6
+ };
7
+ colSpan: 1 | 2 | 3;
8
+ rowSpan: 1;
9
+ colWidth: number;
10
+ gap: number;
11
+ rowHeight: number;
12
+ /** DRAG-06 default 120ms cubic-bezier(0.4, 0, 0.2, 1) on transform. */
13
+ transitionMs?: number;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * Phase 3 DRAG-06 — single 2px snap outline, 120ms cubic-bezier transition on transform.
18
+ *
19
+ * Renders a `position: absolute` 2px solid border rectangle, translated to the
20
+ * target cell via `translate({left}px, {top}px)` where:
21
+ * - `left = cell.col * (colWidth + gap)`
22
+ * - `top = cell.row * (rowHeight + gap)`
23
+ * - `width = colSpan * colWidth + (colSpan - 1) * gap`
24
+ * - `height = rowHeight`
25
+ *
26
+ * Transitions the transform at `${transitionMs ?? 120}ms cubic-bezier(0.4, 0, 0.2, 1)`.
27
+ * Under `prefers-reduced-motion: reduce`, `transition: none !important` strips
28
+ * the slide (SCSS-level override; see snap-outline.scss).
29
+ *
30
+ * Rendering-layer invariants (Plan 05 MUST honor):
31
+ * - `position: absolute` consumer — parent is the grid container with `position: relative`.
32
+ * - `pointer-events: none` (aria-hidden); does NOT intercept drag events.
33
+ * - 2px solid border using `var(--gloss-valid-stroke)` (Phase 0 color token).
34
+ * - Rendered INSIDE the grid container, NOT inside `<DragOverlay>`; DragOverlay
35
+ * hosts the DragGhost only (see Plan 02). The `dropAnimation={null}` invariant
36
+ * on DragOverlay (Plan 00 spike + Plan 01 header) does not apply here — SnapOutline
37
+ * is not a DragOverlay child.
38
+ */
39
+ export declare function SnapOutline({ cell, colSpan, colWidth, gap, rowHeight, transitionMs, className, }: SnapOutlineProps): JSX.Element;
@@ -0,0 +1,2 @@
1
+ export { SnapOutline } from './SnapOutline';
2
+ export type { SnapOutlineProps } from './SnapOutline';
@@ -0,0 +1,82 @@
1
+ /**
2
+ * gridMotionPrimitives — Grid v2 shared motion vocabulary (Phase 0).
3
+ *
4
+ * Single source of truth for all timing, easing, color-token-name, and feedback
5
+ * constants consumed by Phases 1–5 (Grid Foundation, Cascade, Drag Feedback,
6
+ * Drag-to-Edge Auto-Advance, Resize + [×]). Zero behavior — framework-agnostic,
7
+ * no React, no Framer Motion, no hooks. Consumers import plain constants +
8
+ * `getMotionMode()` and decide how to apply them.
9
+ *
10
+ * Requirement mapping:
11
+ * MOTION-01 (no fade-from-nowhere) — documented principle; enforced in review
12
+ * MOTION-02 — cascadePhysics
13
+ * MOTION-03 — swingReveal
14
+ * MOTION-04 — colorTokens.rejection*, colorTokens.valid*, colorTokens.validCellStroke
15
+ * MOTION-05 — rejectionFeedback
16
+ * MOTION-06 — provisional, colorTokens.provisionalWash
17
+ * MOTION-07 — HARD_CLIP
18
+ * MOTION-08 — this file is the lone source; no inline timing/hex/easing in
19
+ * consuming component code (grep-enforced in each consumer phase)
20
+ * MOTION-09 — reducedMotion, getMotionMode()
21
+ *
22
+ * Structural precedent: libs/gloss-ui/src/lib/primitives/overlays/ActionStrip/motion.ts
23
+ */
24
+ /** MOTION-02 — Forward (digest) and reverse (undo) cascade physics. Asymmetric by design. */
25
+ export declare const cascadePhysics: {
26
+ readonly forwardStaggerMs: 50;
27
+ readonly reverseStaggerMs: 30;
28
+ readonly springStiffness: 380;
29
+ readonly springDamping: 32;
30
+ readonly reverseVelocityMultiplier: 1.4;
31
+ };
32
+ /** MOTION-03 — Swing-from-behind reveal (resize handle, [×] affordance, future hover-intent micro-affordances). */
33
+ export declare const swingReveal: {
34
+ readonly enterDurationMs: 220;
35
+ readonly exitDurationMs: 140;
36
+ readonly enterEasing: "cubic-bezier(0.34, 1.56, 0.64, 1)";
37
+ readonly exitEasing: "cubic-bezier(0.4, 0, 1, 1)";
38
+ readonly translateOffsetPx: 4;
39
+ };
40
+ /** MOTION-05 — Rejection kinesthetic: ghost shiver on invalid drop. */
41
+ export declare const rejectionFeedback: {
42
+ readonly shiverCycles: 3;
43
+ readonly shiverAmplitudePx: 2;
44
+ readonly shiverDurationMs: 180;
45
+ readonly validCellOpacity: 0.6;
46
+ };
47
+ /** MOTION-06 — Provisional section/dot materialize + dematerialize. */
48
+ export declare const provisional: {
49
+ readonly materializeMs: 180;
50
+ readonly dematerializeMs: 140;
51
+ readonly easing: "cubic-bezier(0.22, 1, 0.36, 1)";
52
+ readonly dotStrokePx: 2;
53
+ readonly dividerDashGapPx: 4;
54
+ };
55
+ /** MOTION-09 — Substitution values when prefers-reduced-motion: reduce is active. */
56
+ export declare const reducedMotion: {
57
+ readonly cascadeStaggerMs: 0;
58
+ readonly cascadeSpringCapMs: 120;
59
+ readonly swingEnterMs: 100;
60
+ readonly swingEnterStyle: "opacity";
61
+ readonly provisionalMs: 80;
62
+ readonly provisionalStyle: "opacity";
63
+ readonly shiverMs: 200;
64
+ readonly shiverStyle: "color-flash";
65
+ };
66
+ /**
67
+ * MOTION-07 — Hard-clip marker. True = when fixed neighbor grows over a card,
68
+ * cards clip at advancing edge (no fade). Downstream phases must reference this
69
+ * constant as the canonical source for the hard-clip rule.
70
+ */
71
+ export declare const HARD_CLIP: true;
72
+ /** MOTION-04, MOTION-06 — CSS custom property NAMES (not literals). Consumers use var(...). */
73
+ export declare const colorTokens: {
74
+ readonly rejectionFill: "var(--gloss-rejection-fill)";
75
+ readonly rejectionStroke: "var(--gloss-rejection-stroke)";
76
+ readonly validFill: "var(--gloss-valid-fill)";
77
+ readonly validStroke: "var(--gloss-valid-stroke)";
78
+ readonly validCellStroke: "var(--gloss-valid-cell-stroke)";
79
+ readonly provisionalWash: "var(--gloss-provisional-wash)";
80
+ };
81
+ /** MOTION-09 — Motion mode resolver. Pure; SSR-safe; returns 'full' when window is undefined. */
82
+ export declare function getMotionMode(): 'full' | 'reduced';
@@ -17,3 +17,7 @@ export * from './DropAnchorCell';
17
17
  export * from './SpilloverArc';
18
18
  export * from './SectionDivider';
19
19
  export * from './EmptyCellPackAffordance';
20
+ export * from './DragGhost';
21
+ export * from './SnapOutline';
22
+ export * from './CascadePreview';
23
+ export * from './gridMotionPrimitives';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mt-gloss/ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "restricted"