@mt-gloss/ui 0.1.7 → 0.1.9

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.
@@ -4,11 +4,28 @@
4
4
  * Renders `count` buttons inside a `role="tablist"`. Click dispatches
5
5
  * `onSelect(index)`. Keyboard ← → loop forward/backward with wrap semantics
6
6
  * (D-25). Hollow default, filled for the current section.
7
+ *
8
+ * Phase 4 — drag-to-edge-auto-advance — additive extension per D-09/D-10/D-11:
9
+ * - `provisionalIndex` marks an unconfirmed section with hollow-stroke dot
10
+ * (`data-state='provisional-hollow'`) and `aria-label='Unconfirmed section'`.
11
+ * - `pulseIndex` triggers the refuse-pulse animation (`data-state='pulse'`).
12
+ * - `pulseNonce` is composited into the React `key` for the pulsed index so a
13
+ * nonce bump forces a re-mount — which restarts the CSS keyframe animation
14
+ * even if the same index is pulsed twice in a row.
15
+ *
16
+ * All new props are optional; omitting them preserves the Phase 23 two-state
17
+ * `data-state='current'|'hollow'` render exactly (parity contract).
7
18
  */
8
19
  export interface SectionDotsProps {
9
20
  count: number;
10
21
  currentIndex: number;
11
22
  onSelect: (index: number) => void;
12
23
  'aria-label'?: string;
24
+ /** Phase 4 D-09 — index of the unconfirmed ("provisional") section, if any. */
25
+ provisionalIndex?: number;
26
+ /** Phase 4 D-09 — index of the dot currently playing the refuse pulse, if any. */
27
+ pulseIndex?: number;
28
+ /** Phase 4 D-10 — monotonically-increasing nonce; bump to replay the pulse keyframe. */
29
+ pulseNonce?: number;
13
30
  }
14
- export declare function SectionDots({ count, currentIndex, onSelect, 'aria-label': ariaLabel, }: SectionDotsProps): import("react/jsx-runtime").JSX.Element;
31
+ export declare function SectionDots({ count, currentIndex, onSelect, 'aria-label': ariaLabel, provisionalIndex, pulseIndex, pulseNonce, }: SectionDotsProps): import("react/jsx-runtime").JSX.Element;
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- export type SectionDividerState = 'hidden' | 'revealed' | 'emphasized';
2
+ export type SectionDividerState = 'hidden' | 'revealed' | 'emphasized' | 'provisional';
3
3
  export interface SectionDividerProps extends React.HTMLAttributes<HTMLDivElement> {
4
4
  state: SectionDividerState;
5
5
  /** Left-to-right cascade position (0, 1, 2, ...) multiplied by staggerMs for animation-delay. */
@@ -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';
@@ -17,4 +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';
20
23
  export * from './gridMotionPrimitives';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mt-gloss/ui",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "restricted"