@mt-gloss/ui 0.0.47 → 0.0.48

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.
@@ -19,8 +19,6 @@ export { SparklineBg } from '../primitives/dashboard/MetricCard';
19
19
  export { BarsBg } from '../primitives/dashboard/MetricCard';
20
20
  export { DonutChart } from '../primitives/dashboard/MetricCard';
21
21
  export { ObjectArrow } from '../primitives/dashboard/MetricCard';
22
- export { useSlideOutReveal } from '../primitives/dashboard/MetricCard';
23
- export type { SlideOutRevealConfig, SlideOutRevealReturn } from '../primitives/dashboard/MetricCard/useSlideOutReveal';
24
22
  export { TimeFrame, HybridDatePicker } from '../primitives/dashboard/TimeFrame';
25
23
  export type { TimeFrameProps, HybridDatePickerProps } from '../primitives/dashboard/TimeFrame';
26
24
  export { resolvePreset, resolveEvent, smartDateFormat, formatDateRange as formatDateRangeInterpretation } from '@mt-gloss/utils/dateInterpretation';
@@ -17,6 +17,6 @@ import { CardShellProps } from './types';
17
17
  * ```
18
18
  */
19
19
  export declare const CardShell: {
20
- ({ contentSlot, children, colSpan, isStale, isRetrying, beta, disabled, style, accentRing, onGraph, onDetails, onCustomize, onRemove, onWhatsThis, onExpand, onShrink, onMove, onReplace, onNavigate, onRetry, onRefresh, enableQuickSettings, onUpdateTimeframe, instanceTimeframe, cardTitle, quickConfigSlot, onViewAsStack, onUnstack, isStackedGroup, onUpdateStackedTimeframes, instanceStackedTimeframes, isGraphActive, isTableActive, isGraphCard, canExpand, anchorX, extensionOverflow, cardRef: externalCardRef, }: CardShellProps): import("react/jsx-runtime").JSX.Element;
20
+ ({ contentSlot, children, colSpan, isStale, isRetrying, beta, disabled, style, accentRing, onGraph, onDetails, onCustomize, onRemove, onWhatsThis, onExpand, onShrink, onMove, onReplace, onNavigate, onRetry, onRefresh, enableQuickSettings, onUpdateTimeframe, instanceTimeframe, cardTitle, quickConfigSlot, onViewAsStack, onUnstack, isStackedGroup, onUpdateStackedTimeframes, instanceStackedTimeframes, isGraphActive, isTableActive, isGraphCard, canExpand, anchorX, extensionOverflow, cardRef: externalCardRef, hovered, }: CardShellProps): import("react/jsx-runtime").JSX.Element;
21
21
  displayName: string;
22
22
  };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Test-only duplicate of
3
+ * reptime-cloud/react-dashboard/app/components/MetricCards/gridHoverListeners.ts
4
+ *
5
+ * Phase 19.2 Plan 19.2-03 Task 3 — the production helper lives in the reptime
6
+ * consumer repo because the grid-level pointerover listener wiring is reptime-
7
+ * scoped (MetricCardGrid is not part of @mt-gloss/ui). For the NYQUIST-10
8
+ * invariant runner to mount + exercise the listener inside gloss-ui's jsdom
9
+ * test environment, we mirror the helper here.
10
+ *
11
+ * RULE: This file MUST stay byte-for-byte equivalent in behavior to the reptime
12
+ * source. When the reptime helper changes (debounce semantics, drag freeze,
13
+ * disabled filter), update this copy as well. The file exists ONLY to support
14
+ * the `hoveredCardId.invariant.test.tsx` harness — do NOT import it from any
15
+ * production gloss file.
16
+ *
17
+ * If gloss-ui ever ships a MetricCardGrid primitive of its own, promote the
18
+ * helper to a shared location and retire both copies.
19
+ */
20
+ export declare const HOVER_IDLE_DEBOUNCE_MS = 100;
21
+ export interface InstallGridHoverListenersOptions {
22
+ onHoveredCardIdChange: (nextId: string | null) => void;
23
+ draggedCardIdRef: {
24
+ current: string | null;
25
+ };
26
+ debounceMs?: number;
27
+ isDisabledCard?: (el: Element) => boolean;
28
+ }
29
+ export declare function installGridHoverListeners(root: HTMLElement, options: InstallGridHoverListenersOptions): () => void;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * NYQUIST-10 — Phase 19.2 Cross-Card Hover-State Unification behavior matrix fixture.
3
+ *
4
+ * Parametrized behavior definitions that the NYQUIST-10 runner iterates over
5
+ * via `it.each(BEHAVIOR_MATRIX)`. Each row pins one contract-level invariant
6
+ * asserted by Phase 19.2 plans 19.2-01 / 19.2-02 / 19.2-03. The matrix acts as a
7
+ * high-level regression net: if a future wave regresses ANY of the 10 rows,
8
+ * the runner fails with a specific row label even if the underlying test
9
+ * file lives elsewhere.
10
+ *
11
+ * Orthogonal axes (per VALIDATION.md §Orthogonal Sampling Matrix):
12
+ * - gridSize: 3 | 6 | 9 cards (leak-rate scaling)
13
+ * - intervalMs: 8 | 16 | 32 ms (event interval vs frame boundary)
14
+ * - eventCount: 10 | 20 | 40 (min signal / debug baseline / worst case)
15
+ * - cardType: standard | stackable | mixed (StackedGroupCard hover unit)
16
+ * - dndActive: boolean (dnd-kit drag suppression, RESEARCH Pitfall 4)
17
+ * - reducedMotion: boolean (prefers-reduced-motion parity)
18
+ *
19
+ * Row-count pin: the runner asserts exactly 10 rows so that accidentally
20
+ * dropping or silently renaming a behavior fails fast.
21
+ *
22
+ * Invariant each row asserts (after 200ms settle — D-16):
23
+ * 1. ≤1 card has `[data-visible="true"]` on its ActionStrip
24
+ * 2. Every non-hovered card's `useEdgeHoverResize` state === 'idle'
25
+ * 3. `hoveredCardId === expectedFinalCardId || null`
26
+ * 4. `vi.getTimerCount() === 0`
27
+ *
28
+ * @see .planning/phases/19.2-cross-card-hover-state-unification/19.2-CONTEXT.md (D-16, D-17, D-18)
29
+ * @see .planning/phases/19.2-cross-card-hover-state-unification/19.2-VALIDATION.md §Orthogonal Sampling Matrix
30
+ * @see .planning/phases/19.2-cross-card-hover-state-unification/19.2-PATTERNS.md Analog 8 (nyquist-11-fixtures.ts)
31
+ */
32
+ export interface NyquistHarness {
33
+ /** Root grid container element (for dispatchEvent). */
34
+ gridRoot: HTMLElement;
35
+ /** Ordered list of card root elements (each has data-instance-id). */
36
+ cards: HTMLElement[];
37
+ /**
38
+ * Returns current hoveredCardId value from the grid (reads from
39
+ * data-hovered-card-id attribute on root, or a test-only prop hook).
40
+ */
41
+ getHoveredCardId: () => string | null;
42
+ /**
43
+ * Returns the useEdgeHoverResize state for a given card id — reads
44
+ * data-resize-state attribute from slot root.
45
+ */
46
+ getEdgeHoverStateFor: (cardId: string) => string;
47
+ /** Cleanup harness-mounted DOM. */
48
+ unmount: () => void;
49
+ }
50
+ export interface BehaviorParams {
51
+ gridSize: 3 | 6 | 9;
52
+ intervalMs: 8 | 16 | 32;
53
+ eventCount: 10 | 20 | 40;
54
+ cardType: 'standard' | 'stackable' | 'mixed';
55
+ dndActive: boolean;
56
+ reducedMotion: boolean;
57
+ }
58
+ export interface BehaviorDef {
59
+ /** 1-indexed for human readability in test output. */
60
+ id: number;
61
+ /** Short, greppable label. */
62
+ label: string;
63
+ /** Orthogonal axis values for this row. */
64
+ params: BehaviorParams;
65
+ /**
66
+ * The contract invariant this row pins. Either an assert-lambda that the
67
+ * runner invokes inside `it.each`, or `null` when the row is documented as
68
+ * covered by a sibling test file (runner skips but keeps the row in the
69
+ * matrix count pin). Wave 0 ships with `null` assert bodies; Task 2 wires
70
+ * the invariant body via it.each + harness mount, NOT via these callbacks.
71
+ */
72
+ assert: ((harness: NyquistHarness) => Promise<void>) | null;
73
+ /**
74
+ * When present, the runner skips the row with the returned reason string
75
+ * instead of calling `assert`. Used to document rows whose contract is
76
+ * pinned by a dedicated sibling test file (cross-reference maintained in
77
+ * 19.2-VALIDATION.md).
78
+ */
79
+ skipFor?: () => string | undefined;
80
+ }
81
+ /**
82
+ * The 10 NYQUIST-10 behaviors. Each row's `assert` is `null` in Wave 0; the
83
+ * invariant body lives in hoveredCardId.invariant.test.tsx driven by
84
+ * `it.each(BEHAVIOR_MATRIX)`. Rows remain in the matrix so the row-count pin
85
+ * (10) catches silent drops or reorder.
86
+ */
87
+ export declare const BEHAVIOR_MATRIX: BehaviorDef[];
88
+ /**
89
+ * Row-count pin — NYQUIST-10 runner asserts exactly this number of rows.
90
+ * Changing the behavior set requires updating this constant and adding a
91
+ * line in 19.2-VALIDATION.md §Orthogonal Sampling Matrix.
92
+ */
93
+ export declare const BEHAVIOR_ROW_COUNT = 10;
@@ -8,7 +8,6 @@ export { StatusSlotChipMenu } from './StatusSlotChipMenu';
8
8
  export type { StatusSlotChipMenuProps, ChipMenuAction } from './StatusSlotChipMenu';
9
9
  export { EdgeHoverHandle } from './EdgeHoverHandle';
10
10
  export { GhostPreview } from './GhostPreview';
11
- export { useSlideOutReveal } from './useSlideOutReveal';
12
11
  export { useEdgeHoverResize } from './useEdgeHoverResize';
13
12
  export { DEFAULT_RESIZE_PHYSICS } from './resizePhysics';
14
13
  export type { ResizePhysicsConfig } from './resizePhysics';
@@ -18,7 +17,6 @@ export * from './flipAndStage';
18
17
  export * from './types';
19
18
  export type { CardBackProps } from './CardBack';
20
19
  export type { StackedGroupCardProps, StackedValueEntry, SlotActionStripConfig, SlotToActionStripConfig, } from './StackedGroupCard';
21
- export type { SlideOutRevealConfig, SlideOutRevealReturn } from './useSlideOutReveal';
22
20
  export type { ResizeState, ResizeDirection, ResizeMode, ResizeCommit, UseEdgeHoverResizeArgs, UseEdgeHoverResizeReturn, } from './useEdgeHoverResize';
23
21
  export type { EdgeHoverHandleProps } from './EdgeHoverHandle';
24
22
  export type { GhostPreviewProps } from './GhostPreview';
@@ -136,6 +136,21 @@ export interface CardShellProps {
136
136
  beta?: boolean;
137
137
  disabled?: boolean;
138
138
  style?: CSSProperties;
139
+ /**
140
+ * Optional controlled hover state (Phase 19.2 D-02 / D-03 / D-08).
141
+ *
142
+ * When provided, CardShell DOES NOT track hover internally. Grid-authoritative
143
+ * consumers (e.g. `MetricCardGrid` in reptime-cloud) derive hover from a
144
+ * single container-level `pointerover`/`pointerleave` listener and thread
145
+ * `hovered={id === hoveredCardId}` to each slot (D-15).
146
+ *
147
+ * When undefined (default; backward-compat), falls back to internal
148
+ * `useState` driven by `onMouseEnter`/`onMouseLeave`, with the legacy 180ms
149
+ * leave-delay REMOVED (D-08 — CSS animation handles the fade).
150
+ *
151
+ * @default undefined (uncontrolled)
152
+ */
153
+ hovered?: boolean;
139
154
  /**
140
155
  * When true, renders a 2px accent ring around the card shell using box-shadow
141
156
  * (NOT border, to avoid reflow). Used to indicate config mode is active (D-09).
@@ -38,20 +38,35 @@ export interface UseEdgeHoverResizeArgs {
38
38
  */
39
39
  maxColSpan?: 1 | 2 | 3;
40
40
  /**
41
- * Polish Fix #7 (0.0.46 UAT) optional slot-scoped container element.
42
- * When provided, the hook installs a window-level pointermove listener
43
- * during 'dwelling' and 'handle-visible' states. If the cursor's target
44
- * is NOT within `containerRef.current`, the hook force-calls
45
- * `leaveEdgeZone()` — self-heal that fires when a consumer's own
46
- * pointerleave guard (e.g. MetricCardSlot's `closest('.edge-hover-sentinel')`)
47
- * incorrectly matches a SIBLING card's sentinel during a rapid sweep,
48
- * leaving this card stuck at 'handle-visible' while the pointer has
49
- * already moved on. Drag/settling are exempt (pointer capture owns the
50
- * gesture); the self-heal only resets pre-gesture hover state.
41
+ * @deprecated since Phase 19.2superseded by `forceIdle`; retained for
42
+ * consumer-side compat until Phase 19.2 cutover ships. The window-level
43
+ * pointermove self-heal ("Polish Fix #7") that used this ref has been
44
+ * removed (D-04 ¶2). New code should pass `forceIdle` instead.
45
+ *
46
+ * Historical note: When provided, the hook installed a window-level
47
+ * pointermove listener during 'dwelling' and 'handle-visible' states to
48
+ * detect the cursor leaving `containerRef.current`. This was fragile under
49
+ * cross-card sweeps grid-driven `forceIdle` is the correct pattern.
51
50
  */
52
51
  containerRef?: React.RefObject<Element | null> | {
53
52
  current: Element | null;
54
53
  };
54
+ /**
55
+ * Phase 19.2 D-04 — grid-driven synchronous idle reset.
56
+ *
57
+ * When `true`, the hook synchronously clears pending dwell/touch-idle
58
+ * timers and transitions state to `'idle'`. Protects the drag grip:
59
+ * no-op during `'dragging'` or `'settling'` states (pointer-captured;
60
+ * spring owns settle; see D-05 + D-11 + debug/260419-hover-state-stuck-multi-card.md).
61
+ *
62
+ * Replaces the legacy "Polish Fix #7" window-pointermove self-heal
63
+ * (now deleted — grid-driven beats window-driven; D-04 ¶2).
64
+ *
65
+ * Prop-driven only. DO NOT add a ref-based imperative path (D-05).
66
+ *
67
+ * @default undefined (treated as false — hook runs normally)
68
+ */
69
+ forceIdle?: boolean;
55
70
  }
56
71
  export interface UseEdgeHoverResizeReturn {
57
72
  state: ResizeState;
@@ -113,4 +128,4 @@ export interface UseEdgeHoverResizeReturn {
113
128
  * - Bug E handle pointerenter keeps zone alive on sentinel→handle crossover
114
129
  * - Dwell timer cleanup on unmount
115
130
  */
116
- export declare function useEdgeHoverResize({ currentColSpan, stackable, onResize, onCancel, step, gap, tuning, dwellMs, enterMs, exitMs, maxColSpan, containerRef, }: UseEdgeHoverResizeArgs): UseEdgeHoverResizeReturn;
131
+ export declare function useEdgeHoverResize({ currentColSpan, stackable, onResize, onCancel, step, gap, tuning, dwellMs, enterMs, exitMs, maxColSpan, containerRef, forceIdle, }: UseEdgeHoverResizeArgs): UseEdgeHoverResizeReturn;
@@ -1,7 +1,5 @@
1
1
  export { ActionStrip } from './ActionStrip';
2
2
  export type { PrimaryAction, OverflowAction, ActionStripProps, ActionStripContext, ActionStripMenuDirection, } from './types';
3
3
  export { subscribe, getActive, setActive, resetForTests, } from './overflowMenuRegistry';
4
- export { useSlideOutReveal } from './useSlideOutReveal';
5
- export type { SlideOutRevealConfig, SlideOutRevealReturn, SlideOutRevealStyle, } from './useSlideOutReveal';
6
4
  export { DEFAULT_OVERFLOW_IDS, DEFAULT_FOOTER_IDS, buildDefaultOverflow, } from './defaultActions';
7
5
  export type { DefaultHandlers } from './defaultActions';
@@ -90,7 +90,11 @@ export interface ActionStripProps {
90
90
  primary: PrimaryAction[];
91
91
  /** Overflow entries (Settings, Remove, Replace, Refresh, What's this?). */
92
92
  overflow: OverflowAction[];
93
- /** From `useSlideOutReveal` — true when the consumer card is hovered/focused. */
93
+ /**
94
+ * True when the consumer card is hovered/focused. Drives the CSS-owned
95
+ * slide-reveal animation via the root motion.div's `data-visible` attribute
96
+ * (Phase 19.2 D-07 / D-07a — see ActionStrip.scss @keyframes).
97
+ */
94
98
  visible: boolean;
95
99
  /** Sticky-on-click expansion state. */
96
100
  expanded: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mt-gloss/ui",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "restricted"