@mt-gloss/ui 0.0.22 → 0.0.24

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 (29) hide show
  1. package/{Expandable-BCEAQsJY.js → Expandable-GaZcfHDZ.js} +5040 -4944
  2. package/catalog.js +92 -91
  3. package/index.d.ts +4 -0
  4. package/index.js +974 -848
  5. package/lib/facade/data-display.d.ts +2 -2
  6. package/lib/primitives/dashboard/MetricCard/CardShell.d.ts +1 -1
  7. package/lib/primitives/dashboard/MetricCard/flipAndStage/types.d.ts +20 -0
  8. package/lib/primitives/dashboard/MetricCard/index.d.ts +0 -2
  9. package/lib/primitives/dashboard/MetricCard/overflowMenuRegistry.d.ts +1 -25
  10. package/lib/primitives/dashboard/MetricCard/types.d.ts +17 -3
  11. package/lib/primitives/dashboard/MetricCard/useSlideOutReveal.d.ts +1 -42
  12. package/lib/primitives/dashboard/ReconfigBacksideButtons/ReconfigBacksideButtons.d.ts +13 -0
  13. package/lib/primitives/dashboard/ReconfigBacksideButtons/__tests__/fixtures/nyquist-10-matrix.d.ts +19 -0
  14. package/lib/primitives/dashboard/ReconfigBacksideButtons/index.d.ts +5 -0
  15. package/lib/primitives/dashboard/ReconfigBacksideButtons/types.d.ts +47 -0
  16. package/lib/primitives/dashboard/index.d.ts +1 -0
  17. package/lib/primitives/overlays/ActionStrip/ActionStrip.d.ts +9 -0
  18. package/lib/primitives/overlays/ActionStrip/__tests__/fixtures/nyquist-09-parity-matrix.d.ts +18 -0
  19. package/lib/primitives/overlays/ActionStrip/defaultActions.d.ts +32 -0
  20. package/lib/primitives/overlays/ActionStrip/index.d.ts +7 -0
  21. package/lib/primitives/overlays/ActionStrip/motion.d.ts +148 -0
  22. package/lib/primitives/overlays/ActionStrip/overflowMenuRegistry.d.ts +30 -0
  23. package/lib/primitives/overlays/ActionStrip/types.d.ts +115 -0
  24. package/lib/primitives/overlays/ActionStrip/useActionStripRegistry.d.ts +7 -0
  25. package/lib/primitives/overlays/ActionStrip/useSlideOutReveal.d.ts +47 -0
  26. package/lib/primitives/overlays/index.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/ui.css +1 -1
  29. package/lib/primitives/dashboard/MetricCard/GutterActions.d.ts +0 -89
@@ -10,11 +10,11 @@ export { CardShell } from '../primitives/dashboard/MetricCard';
10
10
  export { TrendIndicator } from '../primitives/dashboard/MetricCard';
11
11
  export { StackedGroupCard } from '../primitives/dashboard/MetricCard';
12
12
  export { CardBack } from '../primitives/dashboard/MetricCard';
13
- export { GutterActions } from '../primitives/dashboard/MetricCard';
14
13
  export type { MetricCardProps, CardShellProps } from '../primitives/dashboard/MetricCard/types';
15
14
  export type { StackedGroupCardProps, StackedValueEntry } from '../primitives/dashboard/MetricCard/StackedGroupCard';
16
15
  export type { CardBackProps } from '../primitives/dashboard/MetricCard/CardBack';
17
- export type { GutterActionsProps } from '../primitives/dashboard/MetricCard/GutterActions';
16
+ export { ActionStrip, buildDefaultOverflow } from '../primitives/overlays/ActionStrip';
17
+ export type { ActionStripProps, PrimaryAction, OverflowAction, ActionStripContext, ActionStripMenuDirection, DefaultHandlers, } from '../primitives/overlays/ActionStrip';
18
18
  export { SparklineBg } from '../primitives/dashboard/MetricCard';
19
19
  export { BarsBg } from '../primitives/dashboard/MetricCard';
20
20
  export { DonutChart } from '../primitives/dashboard/MetricCard';
@@ -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, 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, }: CardShellProps): import("react/jsx-runtime").JSX.Element;
21
21
  displayName: string;
22
22
  };
@@ -36,6 +36,13 @@ export interface FlipAndStageInternalState {
36
36
  activeCardId: string | null;
37
37
  activeDimensionId: string | null;
38
38
  dirtyState: Record<string, unknown>;
39
+ /**
40
+ * Phase 17: which dimension section the overlay should render.
41
+ * `null` means no section is open. Independent of `state` — the overlay
42
+ * is only shown after an explicit setOpenSection() call; `cancelConfig()`
43
+ * and `commitConfig()` MUST null this out (per 17-UI-SPEC §Sequencing).
44
+ */
45
+ openSection: string | null;
39
46
  }
40
47
  /**
41
48
  * The full value exposed by useFlipAndStage and FlipAndStageContext.
@@ -48,6 +55,8 @@ export interface FlipAndStageInternalState {
48
55
  * - `commitConfig` — persist edits and return to idle; optional patch merged into dirty
49
56
  * - `cancelConfig` — discard edits and return to idle
50
57
  * - `setDirty` — merge additional edits into dirtyState
58
+ * - `openSection` — (Phase 17) which dimension section the overlay should render
59
+ * - `setOpenSection` — (Phase 17) mutate openSection; cancel/commit auto-null it
51
60
  */
52
61
  export interface FlipAndStageValue {
53
62
  activeCardId: string | null;
@@ -70,6 +79,17 @@ export interface FlipAndStageValue {
70
79
  cancelConfig: () => void;
71
80
  /** Merge additional dirty fields. Successive calls accumulate (not replace). */
72
81
  setDirty: (patch: Record<string, unknown>) => void;
82
+ /**
83
+ * Phase 17: which dimension section the overlay should render.
84
+ * `null` = no section open. Independent of `state` — the overlay is only
85
+ * rendered once a consumer explicitly calls `setOpenSection(id)`.
86
+ */
87
+ openSection: string | null;
88
+ /**
89
+ * Phase 17: mutate openSection. `cancelConfig()` and `commitConfig()` MUST
90
+ * null this out as part of their state reset (per 17-UI-SPEC §Sequencing).
91
+ */
92
+ setOpenSection: (id: string | null) => void;
73
93
  }
74
94
  /**
75
95
  * Options for useFlipAndStage.
@@ -2,7 +2,6 @@ export { MetricCard } from './MetricCard';
2
2
  export { CardShell } from './CardShell';
3
3
  export { CardBack } from './CardBack';
4
4
  export { TrendIndicator } from './TrendIndicator';
5
- export { GutterActions } from './GutterActions';
6
5
  export { StackedGroupCard } from './StackedGroupCard';
7
6
  export { StatusModeCard } from './StatusModeCard';
8
7
  export { StatusSlotChipMenu } from './StatusSlotChipMenu';
@@ -14,7 +13,6 @@ export { useEdgeHoverResize } from './useEdgeHoverResize';
14
13
  export * from './flipAndStage';
15
14
  export * from './types';
16
15
  export type { CardBackProps } from './CardBack';
17
- export type { GutterActionsProps } from './GutterActions';
18
16
  export type { StackedGroupCardProps, StackedValueEntry } from './StackedGroupCard';
19
17
  export type { SlideOutRevealConfig, SlideOutRevealReturn } from './useSlideOutReveal';
20
18
  export type { ResizeState, ResizeDirection, ResizeMode, ResizeCommit, UseEdgeHoverResizeArgs, UseEdgeHoverResizeReturn, } from './useEdgeHoverResize';
@@ -1,25 +1 @@
1
- /**
2
- * overflowMenuRegistry — Module-scoped singleton tracker for MetricCard overflow menus.
3
- *
4
- * quick-260417-ago Cluster C (item 14): only one overflow menu may be open at a time.
5
- * When menu B opens, menu A receives a notification via its useSyncExternalStore
6
- * subscription and closes itself.
7
- *
8
- * Uses a plain module-level `activeId` + Set<listener> — no new dep (Zustand is
9
- * over-engineering for one state field; Context would require hoisting the
10
- * provider higher than the card tree, which complicates flag-off paths).
11
- *
12
- * React 19's useSyncExternalStore consumes `subscribe` / `getSnapshot` and keeps
13
- * components in sync without triggering tearing under concurrent rendering.
14
- *
15
- * Exports:
16
- * - subscribe(fn): listen to activeId changes; returns unsubscribe
17
- * - getActive(): current activeId (or null)
18
- * - setActive(id | null): declare which menu is open; notifies all subscribers
19
- * - resetForTests(): clears state between vitest runs
20
- */
21
- export declare function subscribe(listener: () => void): () => void;
22
- export declare function getActive(): string | null;
23
- export declare function setActive(next: string | null): void;
24
- /** Test-only: resets internal state. Not exported from the public barrel. */
25
- export declare function resetForTests(): void;
1
+ export * from '../../overlays/ActionStrip/overflowMenuRegistry';
@@ -178,15 +178,29 @@ export interface CardShellProps {
178
178
  isGraphCard?: boolean;
179
179
  canExpand?: boolean;
180
180
  /**
181
- * Horizontal anchor (px from card's left edge) forwarded to GutterActions.
181
+ * Horizontal anchor (px from card's left edge) forwarded to ActionStrip.
182
182
  *
183
- * When provided, overrides GutterActions' default `left: 50%` centered pill
183
+ * When provided, overrides ActionStrip's default `left: 50%` centered pill
184
184
  * position. Drives the per-slot gutter-slide choreography for stack cards
185
185
  * (Cluster W quick-260417-d5z item 3 / gutter hover slide). Undefined
186
186
  * preserves the pre-existing centered layout.
187
187
  *
188
- * @see GutterActionsProps.anchorX
188
+ * @see ActionStripProps.anchorX
189
189
  */
190
190
  anchorX?: number;
191
+ /**
192
+ * Consumer-supplied overflow menu entries that CardShell concatenates into
193
+ * the ActionStrip `overflow` prop after the default set (settings / remove
194
+ * / replace / refresh) and before the footer `whats-this`.
195
+ *
196
+ * Per D-04 (variant ownership), gloss primitives stay reptime-agnostic:
197
+ * extensions like Move / Customize are constructed at the reptime
198
+ * consumer and passed in via this prop (usually sourced from
199
+ * `CARD_MENU_EXTENSIONS.getExtensions(cardType, baseOrder)` per AP-09).
200
+ *
201
+ * Phase 16 plan 16-06 Task 3 adds this prop; reptime adopts it for
202
+ * cardType-specific Move / Customize opt-in.
203
+ */
204
+ extensionOverflow?: import('../../overlays/ActionStrip').OverflowAction[];
191
205
  cardRef?: RefObject<HTMLDivElement | null>;
192
206
  }
@@ -1,42 +1 @@
1
- type SlideDirection = 'down' | 'left';
2
- type AnimationPhase = 'hidden' | 'entering-behind' | 'entering-front' | 'visible' | 'exiting-front' | 'exiting-behind';
3
- export interface SlideOutRevealConfig {
4
- slideDirection?: SlideDirection;
5
- behindDuration?: number;
6
- frontDuration?: number;
7
- easing?: string;
8
- behindEasing?: string;
9
- zBehind?: number;
10
- zFront?: number;
11
- }
12
- export interface SlideOutRevealStyle {
13
- transform: string;
14
- zIndex: number;
15
- transition: string;
16
- pointerEvents: 'auto' | 'none';
17
- }
18
- export interface SlideOutRevealReturn {
19
- phase: AnimationPhase;
20
- handleTransitionEnd: () => void;
21
- style: SlideOutRevealStyle;
22
- }
23
- /**
24
- * useSlideOutReveal -- 6-state slide-out animation hook.
25
- *
26
- * Two-phase reveal: element slides from behind a parent surface (z-index behind),
27
- * then once it emerges, z-index switches to front and it settles at resting position.
28
- * Exit is the reverse sequence.
29
- *
30
- * @example
31
- * ```ts
32
- * import { useSlideOutReveal } from '@mt-gloss/ui';
33
- *
34
- * const { phase, handleTransitionEnd, style } = useSlideOutReveal(isVisible, {
35
- * slideDirection: 'down',
36
- * zBehind: 1,
37
- * zFront: 16,
38
- * });
39
- * ```
40
- */
41
- export declare function useSlideOutReveal(visible: boolean, config?: SlideOutRevealConfig): SlideOutRevealReturn;
42
- export {};
1
+ export * from '../../overlays/ActionStrip/useSlideOutReveal';
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ import { ReconfigBacksideButtonsProps, ReconfigDimension } from './types';
3
+ /**
4
+ * Verbatim button labels per 17-UI-SPEC §Copywriting Contract.
5
+ * Exported as a const to make future localization extraction a single edit.
6
+ */
7
+ declare const LABELS: Record<ReconfigDimension, string>;
8
+ /**
9
+ * ReconfigBacksideButtons — renders the type-picker button strip on a
10
+ * metric-card backside. See module doc for parent wiring contract.
11
+ */
12
+ export declare const ReconfigBacksideButtons: React.FC<ReconfigBacksideButtonsProps>;
13
+ export { LABELS as RECONFIG_BACKSIDE_LABELS };
@@ -0,0 +1,19 @@
1
+ import { ReconfigDimension } from '../../types';
2
+ export type Nyquist10Archetype = 'standard' | 'stacked-group' | 'order-status-stack';
3
+ export interface Nyquist10MatrixRow {
4
+ /** cardType the matrix row describes. */
5
+ cardType: Nyquist10Archetype;
6
+ /** Dimension under test (the backside button to click). */
7
+ dimension: ReconfigDimension;
8
+ /** All dimensions expected on the backside for this cardType. */
9
+ expectedBacksideDimensions: ReadonlyArray<ReconfigDimension>;
10
+ /** data-section-id that MUST be the sole rendered section post-click. */
11
+ expectedOverlaySectionId: string;
12
+ }
13
+ export declare const NYQUIST_10_MATRIX: ReadonlyArray<Nyquist10MatrixRow>;
14
+ /** Row count pin consumed by the contract runner. */
15
+ export declare const NYQUIST_10_ROW_COUNT: number;
16
+ /** The 3 archetypes enumerated (stable order, useful for grouped describes). */
17
+ export declare const NYQUIST_10_ARCHETYPES: ReadonlyArray<Nyquist10Archetype>;
18
+ /** Matrix lookup — parity check against reptime's CARD_TYPE_DIMENSION_MATRIX. */
19
+ export declare const NYQUIST_10_DIMENSIONS_BY_CARD_TYPE: Readonly<Record<Nyquist10Archetype, ReadonlyArray<ReconfigDimension>>>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Phase 17, Plan 01 — ReconfigBacksideButtons barrel.
3
+ */
4
+ export { ReconfigBacksideButtons, RECONFIG_BACKSIDE_LABELS } from './ReconfigBacksideButtons';
5
+ export type { ReconfigBacksideButtonsProps, ReconfigDimension } from './types';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Phase 17, Plan 01: ReconfigBacksideButtons primitive — public type contracts.
3
+ *
4
+ * Renders the card-back type-picker strip (one button per configurable
5
+ * dimension). Buttons only — NO dropdowns, NO popovers. See 17-UI-SPEC
6
+ * §Backside type-picker button labels for the full spec.
7
+ *
8
+ * @module primitives/dashboard/ReconfigBacksideButtons/types
9
+ */
10
+ /**
11
+ * The six configurable dimensions exposable on any metric card backside.
12
+ * Per-card-type matrix lives downstream in reptime (`CARD_TYPE_DIMENSION_MATRIX`).
13
+ */
14
+ export type ReconfigDimension = 'timeframe' | 'threshold' | 'slots' | 'size' | 'color' | 'pinned';
15
+ /**
16
+ * Public props for ReconfigBacksideButtons.
17
+ *
18
+ * Minimal by design — no optional prop beyond these four. Parent wires
19
+ * onDimensionSelect to `flipAndStage.setOpenSection(dim)` + `flipToFace()`.
20
+ */
21
+ export interface ReconfigBacksideButtonsProps {
22
+ /**
23
+ * Dimensions to render (order is preserved; empty array → component
24
+ * returns null — parent should avoid rendering at all for excluded types).
25
+ */
26
+ dimensions: ReadonlyArray<ReconfigDimension>;
27
+ /**
28
+ * Fired on button click. Parent wires:
29
+ * flipAndStage.setOpenSection(dimension)
30
+ * flipAndStage.flipToFace()
31
+ * in that order.
32
+ */
33
+ onDimensionSelect: (dimension: ReconfigDimension) => void;
34
+ /**
35
+ * Highlights the active dimension with aria-current="true" + persisted
36
+ * hover tint. Used when re-entering the backside with a previously-
37
+ * launched overlay still in memory.
38
+ */
39
+ activeDimension?: ReconfigDimension | null;
40
+ /**
41
+ * Screen-reader label substitution — rendered as
42
+ * `aria-label="Configure {ariaCardTypeLabel}: choose a dimension"` on root.
43
+ */
44
+ ariaCardTypeLabel?: string;
45
+ /** Optional consumer-provided class appended to the BEM root class. */
46
+ className?: string;
47
+ }
@@ -7,6 +7,7 @@ export * from './UnifiedBreakdownTable';
7
7
  export * from './ChartControlBar';
8
8
  export * from './UnifiedContextMenu';
9
9
  export * from './TimeFrame';
10
+ export * from './ReconfigBacksideButtons';
10
11
  export * from './MetricCardGrid';
11
12
  export * from './TabbedDataView';
12
13
  export * from './MetricGroupContainer';
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ import { ActionStripProps, OverflowAction } from './types';
3
+ /**
4
+ * D5 pure-icon bypass predicate. When every overflow entry is promotable AND
5
+ * all promotable fit the expanded budget, expansion grows the strip + runs icon
6
+ * ingress ONLY — no border-radius morph, no vertical menu surface.
7
+ */
8
+ export declare function isPureIconBypass(overflow: readonly OverflowAction[], expandedBudgetSlots: number): boolean;
9
+ export declare const ActionStrip: React.FC<ActionStripProps>;
@@ -0,0 +1,18 @@
1
+ export type FlipDir = 'up' | 'down';
2
+ export type Variant = 'default' | 'config';
3
+ export interface ParityHarness {
4
+ row: ParityRow;
5
+ }
6
+ export interface ParityRow {
7
+ behaviorId: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
8
+ behaviorLabel: string;
9
+ flip: FlipDir;
10
+ variant: Variant;
11
+ /** Skip reason (row still emits as `test.skip` so it is visible in vitest output). */
12
+ skip?: string;
13
+ /** Concrete assertion executed by `it.each` runner. */
14
+ assert: (harness: ParityHarness) => Promise<void> | void;
15
+ }
16
+ export declare const PARITY_MATRIX: ParityRow[];
17
+ /** Row count = 9 × 2 × 2 = 36. Exposed for assertion convenience in the runner. */
18
+ export declare const MATRIX_ROW_COUNT: number;
@@ -0,0 +1,32 @@
1
+ import { OverflowAction } from './types';
2
+ /**
3
+ * The three promotable default overflow ids, in canonical order. Callsites
4
+ * that want to filter or extend the default set reference these ids directly.
5
+ *
6
+ * Note: `refresh` is part of the D4 catalog but NOT listed here because it is
7
+ * a context-specific action (only fires when a card has a refresh affordance).
8
+ * Consumers that need Refresh pass `onRefresh` — `buildDefaultOverflow` emits
9
+ * the entry when present. For registry-level "is this id part of the default
10
+ * set?" checks, include 'refresh' in the superset.
11
+ */
12
+ export declare const DEFAULT_OVERFLOW_IDS: readonly ["settings", "remove", "replace"];
13
+ /** The single non-promotable footer id. */
14
+ export declare const DEFAULT_FOOTER_IDS: readonly ["whats-this"];
15
+ /** Optional handlers for the default overflow set. Absent handlers omit entries. */
16
+ export interface DefaultHandlers {
17
+ onSettings?: () => void;
18
+ onRemove?: () => void;
19
+ onReplace?: () => void;
20
+ onRefresh?: () => void;
21
+ onWhatsThis?: () => void;
22
+ }
23
+ /**
24
+ * Build the D4 default overflow set. Emits one entry per handler provided,
25
+ * preserving canonical order (`settings`, `remove`, `replace`, `refresh`,
26
+ * `whats-this`) and tagging `isPromotable: true` for everything except the
27
+ * footer `whats-this`.
28
+ *
29
+ * @param handlers - per-card handler map; absent handlers omit entries.
30
+ * @returns `OverflowAction[]` in canonical order, `order` monotonic from 0.
31
+ */
32
+ export declare function buildDefaultOverflow(handlers: DefaultHandlers): OverflowAction[];
@@ -0,0 +1,7 @@
1
+ export { ActionStrip } from './ActionStrip';
2
+ export type { PrimaryAction, OverflowAction, ActionStripProps, ActionStripContext, ActionStripMenuDirection, } from './types';
3
+ export { subscribe, getActive, setActive, resetForTests, } from './overflowMenuRegistry';
4
+ export { useSlideOutReveal } from './useSlideOutReveal';
5
+ export type { SlideOutRevealConfig, SlideOutRevealReturn, SlideOutRevealStyle, } from './useSlideOutReveal';
6
+ export { DEFAULT_OVERFLOW_IDS, DEFAULT_FOOTER_IDS, buildDefaultOverflow, } from './defaultActions';
7
+ export type { DefaultHandlers } from './defaultActions';
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ActionStrip — Framer Motion choreography constants (Phase 16 plan 16-02).
3
+ *
4
+ * Every exported constant maps to an exact row in `.planning/phases/16-overflow-menu-reliability/16-UI-SPEC.md`
5
+ * §Motion & Choreography. If a test for the timing table ever fails, either:
6
+ * 1) UI-SPEC was updated and the constant below must change to match, OR
7
+ * 2) the constant was changed accidentally and must be reverted.
8
+ *
9
+ * These values are consumed by `ActionStrip.tsx`; they are exported so tests can
10
+ * assert against them directly (no fragile source-grep of inline numbers).
11
+ *
12
+ * NO automatic-height keyframes anywhere — AP guard enforced by Test 8 in
13
+ * `__tests__/choreography.test.tsx`. Use `scaleY` for menu open/close.
14
+ */
15
+ export declare const TIMING: {
16
+ readonly STRIP_EXPAND_MS: 250;
17
+ readonly STRIP_COLLAPSE_MS: 180;
18
+ readonly ICON_INGRESS_MS: 150;
19
+ readonly ICON_STAGGER_MS: 50;
20
+ readonly ICON_EGRESS_MS: 120;
21
+ readonly MENU_OPEN_MS: 250;
22
+ readonly MENU_CLOSE_MS: 120;
23
+ readonly BORDER_RADIUS_MS: 200;
24
+ readonly ANCHOR_X_MS: 180;
25
+ readonly FEEDBACK_PRESS_MS: 100;
26
+ readonly FEEDBACK_CHECK_MS: 600;
27
+ readonly FLIP_COLLAPSE_MAX_WIDTH_MS: 250;
28
+ readonly FLIP_COLLAPSE_OPACITY_MS: 200;
29
+ readonly GRACE_CLOSE_MS: 300;
30
+ readonly REDUCED_MOTION_MS: 10;
31
+ };
32
+ /** Framer Motion spring physics (UI-SPEC row "Expand (click) — Strip width"). */
33
+ export declare const SPRING: {
34
+ type: "spring";
35
+ stiffness: number;
36
+ damping: number;
37
+ };
38
+ /** UI-SPEC row "Reveal (hover) — front phase" and Expand — Icon ingress: cubic-bezier(0.22, 1, 0.36, 1). */
39
+ export declare const EASE_OUT: readonly [0.22, 1, 0.36, 1];
40
+ type FlipDirection = 'up' | 'down';
41
+ /**
42
+ * Pill border-radius morph. UI-SPEC §Motion & Choreography row "Expand (click)
43
+ * — Border-radius" + §Up/Down Flip Mirror Contract (D7).
44
+ *
45
+ * Three named variants cover: collapsed (8px all-round), expanded-down (bottom
46
+ * corners flat, menu hangs below), expanded-up (top corners flat, menu sits
47
+ * above — mirror of expanded-down).
48
+ */
49
+ export declare function buildPillVariants(reduced: boolean): {
50
+ collapsed: {
51
+ borderRadius: number;
52
+ transition: {
53
+ duration: number;
54
+ ease: "easeOut";
55
+ };
56
+ };
57
+ 'expanded-down': {
58
+ borderRadius: string;
59
+ transition: {
60
+ duration: number;
61
+ ease: "easeOut";
62
+ };
63
+ };
64
+ 'expanded-up': {
65
+ borderRadius: string;
66
+ transition: {
67
+ duration: number;
68
+ ease: "easeOut";
69
+ };
70
+ };
71
+ };
72
+ /**
73
+ * Vertical menu scaleY + opacity + y-offset. UI-SPEC §Motion rows "Expand —
74
+ * Menu drop" + "Collapse — Menu hide" + §Up/Down Flip Mirror "Menu entry
75
+ * y-translation".
76
+ *
77
+ * `custom` is the flip direction — `down` enters from y:-4, `up` from y:+4.
78
+ * Menu is asymmetric: open 250ms ease-out; close 120ms ease-in.
79
+ */
80
+ export declare function buildMenuVariants(reduced: boolean): {
81
+ initial: (dir: FlipDirection) => {
82
+ opacity: number;
83
+ scaleY: number;
84
+ y: number;
85
+ };
86
+ animate: {
87
+ opacity: number;
88
+ scaleY: number;
89
+ y: number;
90
+ transition: {
91
+ duration: number;
92
+ ease: "easeOut";
93
+ };
94
+ };
95
+ exit: {
96
+ opacity: number;
97
+ scaleY: number;
98
+ transition: {
99
+ duration: number;
100
+ ease: "easeIn";
101
+ };
102
+ };
103
+ };
104
+ /**
105
+ * Promoted-icon ingress / egress. UI-SPEC §Motion rows "Expand — Icon ingress"
106
+ * + "Collapse — Icon egress". Stagger is owned by the parent container variant.
107
+ */
108
+ export declare function buildIconVariants(reduced: boolean): {
109
+ initial: {
110
+ opacity: number;
111
+ scale: number;
112
+ };
113
+ animate: {
114
+ opacity: number;
115
+ scale: number;
116
+ transition: {
117
+ duration: number;
118
+ ease: "easeOut";
119
+ };
120
+ };
121
+ exit: {
122
+ opacity: number;
123
+ scale: number;
124
+ transition: {
125
+ duration: number;
126
+ ease: "easeIn";
127
+ };
128
+ };
129
+ };
130
+ /**
131
+ * Parent-container staggerChildren variants. UI-SPEC §Motion row "Expand —
132
+ * Icon ingress: 50ms stagger". Collapse has no stagger (asymmetric per D9).
133
+ */
134
+ export declare function buildStripContainerVariants(reduced: boolean): {
135
+ collapsed: {
136
+ transition: {
137
+ staggerChildren: number;
138
+ staggerDirection: -1;
139
+ };
140
+ };
141
+ expanded: {
142
+ transition: {
143
+ staggerChildren: number;
144
+ };
145
+ };
146
+ };
147
+ export declare function transformOriginFor(dir: FlipDirection): 'top center' | 'bottom center';
148
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * overflowMenuRegistry — Module-scoped singleton tracker for ActionStrip overflow menus.
3
+ *
4
+ * MOVED from `primitives/dashboard/MetricCard/overflowMenuRegistry.ts` in Phase 16
5
+ * plan 16-01 alongside the GutterActions → ActionStrip rewrite. The legacy path
6
+ * re-exports from this module so existing consumers continue to compile during
7
+ * the multi-plan transition.
8
+ *
9
+ * quick-260417-ago Cluster C (item 14): only one overflow menu may be open at a time.
10
+ * When menu B opens, menu A receives a notification via its useSyncExternalStore
11
+ * subscription and closes itself.
12
+ *
13
+ * Uses a plain module-level `activeId` + Set<listener> — no new dep (Zustand is
14
+ * over-engineering for one state field; Context would require hoisting the
15
+ * provider higher than the card tree, which complicates flag-off paths).
16
+ *
17
+ * React 19's useSyncExternalStore consumes `subscribe` / `getSnapshot` and keeps
18
+ * components in sync without triggering tearing under concurrent rendering.
19
+ *
20
+ * Exports:
21
+ * - subscribe(fn): listen to activeId changes; returns unsubscribe
22
+ * - getActive(): current activeId (or null)
23
+ * - setActive(id | null): declare which menu is open; notifies all subscribers
24
+ * - resetForTests(): clears state between vitest runs
25
+ */
26
+ export declare function subscribe(listener: () => void): () => void;
27
+ export declare function getActive(): string | null;
28
+ export declare function setActive(next: string | null): void;
29
+ /** Test-only: resets internal state. Not exported from the public barrel. */
30
+ export declare function resetForTests(): void;
@@ -0,0 +1,115 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Runtime context forwarded to every action's `visibleWhen()` predicate.
4
+ *
5
+ * Known keys reflect today's call-site usage (error gating, card kind, column span,
6
+ * graph card affordance, expand capability). Unknown keys are permitted to keep the
7
+ * extension surface non-breaking as new card types add discriminators.
8
+ */
9
+ export interface ActionStripContext {
10
+ /** True when the card is in a retry/error state — gates Retry vs Graph/Details mutex. */
11
+ isErrorState?: boolean;
12
+ /** Card subtype discriminator (e.g., 'trend', 'statusMode', 'stackedGroup'). */
13
+ cardType?: string;
14
+ /** Column span of the hosting card; some actions (Expand/Shrink) key on this. */
15
+ colSpan?: 1 | 2 | 3;
16
+ /** True when the host card has a graph affordance (enables Expand in menu). */
17
+ isGraphCard?: boolean;
18
+ /** True when the host card is currently expandable (card-level capability gate). */
19
+ canExpand?: boolean;
20
+ /** Extension point for future visibleWhen predicates — additions are non-breaking. */
21
+ [key: string]: unknown;
22
+ }
23
+ /**
24
+ * Pinned strip button. Always present in the collapsed pill when `visibleWhen` passes.
25
+ *
26
+ * Supports feedback animation (pressed → Check flash) and active-state color
27
+ * inversion (`isActive` + `activeColor` + `activeIcon`). Preserved behaviors #6/#7
28
+ * from D3 of the rewrite decisions.
29
+ */
30
+ export interface PrimaryAction {
31
+ /** Stable identifier for test hooks + layoutId assignment. */
32
+ id: string;
33
+ /** Resting icon. */
34
+ icon: ReactNode;
35
+ /** Optional icon swapped in when `isActive` is true (e.g., Check after Graph toggle). */
36
+ activeIcon?: ReactNode;
37
+ /** Tooltip label (native `title=`). */
38
+ label: string;
39
+ /** Click handler. */
40
+ onClick: () => void;
41
+ /** True when the action is the currently-active view (drives color inversion). */
42
+ isActive?: boolean;
43
+ /** When true, render 100ms pressed-state + 600ms Check flash on click. */
44
+ showFeedback?: boolean;
45
+ /** Arbitrary CSS color string for the active state (e.g., '#4f46e5' for Graph). */
46
+ activeColor?: string;
47
+ /** Predicate; action is hidden when returns false. Runs on every render. */
48
+ visibleWhen?: (ctx: ActionStripContext) => boolean;
49
+ }
50
+ /**
51
+ * Overflow menu entry. Rendered in the vertical menu by default; may be promoted
52
+ * into the expanded strip when `isPromotable: true` and there's budget.
53
+ *
54
+ * `order` sets monotonic rank used for promotion priority and menu rendering.
55
+ * The lone non-promotable entry today is "What's this?" (footer).
56
+ */
57
+ export interface OverflowAction {
58
+ /** Stable identifier for test hooks. */
59
+ id: string;
60
+ /** Icon; required only when the action may be promoted into the strip. */
61
+ icon?: ReactNode;
62
+ /** Menu-item text / promoted-button tooltip. */
63
+ label: string;
64
+ /** Click handler. */
65
+ onClick: () => void;
66
+ /** True = eligible for promotion into the expanded strip; false = vertical-only. */
67
+ isPromotable: boolean;
68
+ /** Monotonic rank for promotion priority + vertical render order. */
69
+ order: number;
70
+ /** Predicate; action is hidden when returns false. Runs on every render. */
71
+ visibleWhen?: (ctx: ActionStripContext) => boolean;
72
+ }
73
+ /**
74
+ * Menu-flip direction. Omit to let floating-ui's flip() middleware decide.
75
+ *
76
+ * AP-13 — the reconfig-surface pass-through variant was retired in Phase 17
77
+ * (plan 17-04): its type alias and Save / Cancel pass-through props were
78
+ * removed along with the render branch. Cancel / close now live in the
79
+ * Phase 17 overlay header, not inside ActionStrip. Only lingering references
80
+ * allowed in source: skip-marker strings in NYQUIST-09 parity-matrix fixtures
81
+ * under `__tests__/fixtures/`. Any resurrection in source is a regression and
82
+ * is blocked by `nyquist-10-ap-guards.test.ts` (AP-13 source-grep).
83
+ */
84
+ export type ActionStripMenuDirection = 'up' | 'down';
85
+ /**
86
+ * Public ActionStrip prop contract. Stable across Phase 16 plans 16-01..05.
87
+ */
88
+ export interface ActionStripProps {
89
+ /** Pinned strip buttons (Graph, Details, Navigate, Retry, etc.). */
90
+ primary: PrimaryAction[];
91
+ /** Overflow entries (Settings, Remove, Replace, Refresh, What's this?). */
92
+ overflow: OverflowAction[];
93
+ /** From `useSlideOutReveal` — true when the consumer card is hovered/focused. */
94
+ visible: boolean;
95
+ /** Sticky-on-click expansion state. */
96
+ expanded: boolean;
97
+ /** Called to toggle `expanded`. */
98
+ onExpandedChange: (open: boolean) => void;
99
+ /** CardBack collapse-to-Undo. Preserved behavior #5. */
100
+ isFlipped?: boolean;
101
+ /** Called when the Undo button is clicked while isFlipped. */
102
+ onFlipBack?: () => void;
103
+ /** Stack-card slot-slide horizontal anchor in px. Preserved behavior #3. */
104
+ anchorX?: number;
105
+ /** Optional override; omit to let floating-ui flip() decide. Re-measured at expand click. */
106
+ menuDirection?: ActionStripMenuDirection;
107
+ /** Singleton registry key. Preserved behavior #1. Auto-generated if omitted. */
108
+ menuId?: string;
109
+ /** Runtime context forwarded to action visibleWhen() predicates. */
110
+ context?: ActionStripContext;
111
+ /** Consumer-overridable tooltip for the Graph pinned button. */
112
+ graphLabel?: string;
113
+ /** Consumer-overridable tooltip for the Details pinned button. */
114
+ detailsLabel?: string;
115
+ }