@tensor_1/react 2.48.0 → 2.49.0

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 (47) hide show
  1. package/README.md +70 -1
  2. package/dist/components/Chip.d.ts +7 -3
  3. package/dist/components/Chip.js +2 -2
  4. package/dist/components/DashboardCanvas.js +14 -0
  5. package/dist/components/DataWorkspace.js +1 -4
  6. package/dist/components/FocusOnMount.d.ts +282 -0
  7. package/dist/components/FocusOnMount.js +12 -0
  8. package/dist/components/Kanban.d.ts +14 -3
  9. package/dist/components/Kanban.js +9 -4
  10. package/dist/components/MotionList.d.ts +303 -0
  11. package/dist/components/MotionList.js +175 -0
  12. package/dist/components/Popover.js +9 -0
  13. package/dist/components/RelationshipGraph.js +4 -1
  14. package/dist/components/RelationshipGraphLayered.js +107 -32
  15. package/dist/components/SearchSuggest.js +8 -1
  16. package/dist/components/Segmented.js +8 -2
  17. package/dist/components/Select.js +6 -0
  18. package/dist/components/SortableList.d.ts +2 -2
  19. package/dist/components/SortableList.js +16 -3
  20. package/dist/components/States.d.ts +33 -1
  21. package/dist/components/States.js +43 -1
  22. package/dist/components/Tabs.js +9 -2
  23. package/dist/components/Toast.d.ts +1 -1
  24. package/dist/components/Toast.js +106 -2
  25. package/dist/components/Tooltip.js +62 -7
  26. package/dist/emoji.cjs +115 -14
  27. package/dist/hooks/useAnchoredPosition.js +14 -2
  28. package/dist/hooks/useDeferredReorder.d.ts +32 -0
  29. package/dist/hooks/useDeferredReorder.js +47 -0
  30. package/dist/hooks/useDialog.js +15 -2
  31. package/dist/hooks/useKeyboardGrab.d.ts +58 -0
  32. package/dist/hooks/useKeyboardGrab.js +118 -0
  33. package/dist/hooks/useSlidingIndicator.d.ts +18 -0
  34. package/dist/hooks/useSlidingIndicator.js +80 -0
  35. package/dist/index.cjs +2203 -1373
  36. package/dist/index.d.cts +477 -13
  37. package/dist/index.d.ts +7 -3
  38. package/dist/index.js +14 -2
  39. package/dist/labels-context.d.ts +3 -0
  40. package/dist/motion.d.ts +11 -0
  41. package/dist/motion.js +35 -0
  42. package/dist/relationship-graph-model.d.ts +17 -3
  43. package/dist/relationship-graph-model.js +28 -16
  44. package/dist/rich-text.cjs +42 -2
  45. package/dist/toast-store.d.ts +5 -2
  46. package/dist/toast-store.js +3 -2
  47. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -11,6 +11,9 @@ export { PENDING_DELAY_MS, PENDING_MIN_VISIBLE_MS, RESULT_HOLD_MS } from './moti
11
11
  export { ChangeHighlightOptions, flashChanged, useChangeHighlight } from './hooks/useChangeHighlight.js';
12
12
  export { OptimisticRollback, UseOptimisticValueOptions, UseOptimisticValueResult, useOptimisticValue } from './hooks/useOptimisticValue.js';
13
13
  export { announcePolite } from './announce.js';
14
+ export { UseDeferredReorderResult, useDeferredReorder } from './hooks/useDeferredReorder.js';
15
+ export { useSlidingIndicator } from './hooks/useSlidingIndicator.js';
16
+ export { DEFAULT_KEYBOARD_GRAB_LABELS, KeyboardGrabDirection, KeyboardGrabLabels, UseKeyboardGrabOptions, UseKeyboardGrabResult, settleDrop, useKeyboardGrab } from './hooks/useKeyboardGrab.js';
14
17
  export { Align, Placement, PositionOptions, PositionResult, Rect, Side, Size, computePosition } from './overlay-position.js';
15
18
  export { Button, ButtonGroup, ButtonGroupProps, ButtonLink, ButtonLinkProps, ButtonProps, ButtonSize, ButtonVariant } from './components/Button.js';
16
19
  export { UixDateFormat, UixLabels, UixLabelsProvider, UixLabelsProviderProps, useUixLabels } from './labels-context.js';
@@ -88,7 +91,7 @@ export { Kbd, KbdCombo, KbdComboProps, KbdPlatform, KbdProps, KeyShortcut, ariaK
88
91
  export { DEFAULT_STEP_STATE_LABELS, Step, StepProps, StepState, StepStateLabels, Steps, StepsProps } from './components/Steps.js';
89
92
  export { Tooltip, TooltipProps } from './components/Tooltip.js';
90
93
  export { InfoTip, InfoTipProps } from './components/InfoTip.js';
91
- export { AccessStateProps, EmptyState, EmptyStateProps, ErrorState, ErrorStateProps, FilteredEmptyState, ForbiddenState, LoadingState, LoadingStateProps, NotFoundState, Skeleton, SkeletonProps, StateVariant } from './components/States.js';
94
+ export { AccessStateProps, EmptyState, EmptyStateProps, ErrorState, ErrorStateProps, FilteredEmptyState, ForbiddenState, LoadingState, LoadingStateProps, NotFoundState, Skeleton, SkeletonProps, StateVariant, SuccessState, SuccessStateProps } from './components/States.js';
92
95
  export { RelativeTime, RelativeTimeFormatContext, RelativeTimeProps } from './components/RelativeTime.js';
93
96
  export { RelativeTimeUnit, RelativeTimeValue, parseDateValue, relativeTimeValue } from './relative-time-model.js';
94
97
  export { AsyncOperationState, AsyncOperationStatus, AsyncOperationStatusProps } from './components/AsyncOperationStatus.js';
@@ -99,6 +102,7 @@ export { DEFAULT_SAVE_STATUS_LABELS, SaveStatus, SaveStatusLabels, SaveStatusPro
99
102
  export { BusyRegion, BusyRegionProps } from './components/BusyRegion.js';
100
103
  export { ChangeHighlight, ChangeHighlightProps } from './components/ChangeHighlight.js';
101
104
  export { Count, CountProps } from './components/Count.js';
105
+ export { MotionList, MotionListProps } from './components/MotionList.js';
102
106
  export { ButtonResult, ButtonResultLabels, DEFAULT_BUTTON_RESULT_LABELS } from './components/ButtonFeedback.js';
103
107
  export { Composer, ComposerBar, ComposerBarProps, ComposerProps } from './components/Composer.js';
104
108
  export { LocaleSwitcher, LocaleSwitcherLocale, LocaleSwitcherProps, Segmented, SegmentedOption, SegmentedOptionProps, SegmentedProps } from './components/Segmented.js';
@@ -115,7 +119,7 @@ export { Menu, MenuGroup, MenuGroupProps, MenuItem, MenuItemAttributes, MenuItem
115
119
  export { CommandGroup, CommandGroupProps, CommandItem, CommandItemProps, CommandPalette, CommandPaletteProps } from './components/CommandPalette.js';
116
120
  export { CommandPaletteGroup, CommandPaletteItem, filterCommandItems, groupCommandItems } from './command-palette-model.js';
117
121
  export { Inbox, InboxDetail, InboxDetailProps, InboxItem, InboxItemProps, InboxList, InboxListProps, InboxProps } from './components/Inbox.js';
118
- export { Kanban, KanbanCard, KanbanCardProps, KanbanColumn, KanbanColumnProps, KanbanMoveDirection, KanbanProps } from './components/Kanban.js';
122
+ export { Kanban, KanbanCard, KanbanCardProps, KanbanColumn, KanbanColumnProps, KanbanDropLine, KanbanDropLineProps, KanbanMoveDirection, KanbanProps } from './components/Kanban.js';
119
123
  export { DEFAULT_TREE_LABELS, Tree, TreeLabels, TreeNodeData, TreeNodeProps, TreeProps } from './components/Tree.js';
120
124
  export { DEFAULT_ORG_CHART_LABELS, OrgChart, OrgChartLabels, OrgChartNode, OrgChartProps, orgChartInitials, orgChartZoomStep } from './components/OrgChart.js';
121
125
  export { FlatNode, TreeLike, TreeNavAction, flattenTree, treeNav } from './tree-model.js';
@@ -137,7 +141,7 @@ export { DEFAULT_TIME_PICKER_LABELS, TimeInputProblem, TimePicker, TimePickerLab
137
141
  export { formatTimeKey, isOvernightRange, isTimeOutOfRange, isValidTimeKey, minutesToTime, parseTimeInput, stepTime, timePartEnds, timeSlots, timeToMinutes, uses12Hour } from './time-field-model.js';
138
142
  export { CalendarDay, CalendarWeekday, DateRangeValue, LaneCluster, LaneInterval, LanePlacement, PackLanesOptions, PackedLanes, ZonedDateSpan, ZonedDayBounds, ZonedDayShift, ZonedDaySpan, ZonedHourSlot, addCalendarDays, addCalendarMonths, addZonedDays, buildMonthGrid, cachedDateTimeFormat, enumerateDateKeys, enumerateDateSpan, isDateInRange, isDateUnavailable, packLanes, rankOverflow, selectRangeDate, startOfMonth, toDateKey, zonedDateKey, zonedDateSpan, zonedDayBounds, zonedDaySpan, zonedHourSlots, zonedTimeOfDay } from './calendar-model.js';
139
143
  export { DEFAULT_RELATIONSHIP_GRAPH_LABELS, RelationshipGraph, RelationshipGraphLabels, RelationshipGraphLegendItem, RelationshipGraphProps } from './components/RelationshipGraph.js';
140
- export { BoundedRelationshipGraph, EdgeLabelBox, LayeredColumn, LayeredEdgeClassification, LayeredEdgeKind, LayeredItem, LayeredLayout, LayeredLayoutOptions, LayeredNavigationKey, PlacedEdgeLabel, PositionedRelationshipNode, RelationshipGraphArrow, RelationshipGraphCluster, RelationshipGraphEdge, RelationshipGraphNode, RelationshipGraphTone, RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, edgeLabelSize, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, separateEdgeLabels, traverseRelationshipNode, wrapEdgeLabel } from './relationship-graph-model.js';
144
+ export { BoundedRelationshipGraph, EdgeLabelBox, LabelObstacle, LayeredColumn, LayeredEdgeClassification, LayeredEdgeKind, LayeredItem, LayeredLayout, LayeredLayoutOptions, LayeredNavigationKey, PlacedEdgeLabel, PositionedRelationshipNode, RelationshipGraphArrow, RelationshipGraphCluster, RelationshipGraphEdge, RelationshipGraphNode, RelationshipGraphTone, RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, edgeLabelSize, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, separateEdgeLabels, traverseRelationshipNode, wrapEdgeLabel } from './relationship-graph-model.js';
141
145
  export { AuditLog, AuditLogAction, AuditLogEvent, AuditLogFilters, AuditLogLabels, AuditLogProps, DEFAULT_AUDIT_LOG_LABELS } from './components/AuditLog.js';
142
146
  export { DEFAULT_MATCH_REVIEW_LABELS, MatchBulkResult, MatchReview, MatchReviewAction, MatchReviewCandidate, MatchReviewField, MatchReviewLabels, MatchReviewProps } from './components/MatchReview.js';
143
147
  export { MetricInput, MetricInputProps } from './components/MetricInput.js';
package/dist/index.js CHANGED
@@ -20,6 +20,9 @@ import { PENDING_DELAY_MS, PENDING_MIN_VISIBLE_MS, RESULT_HOLD_MS } from "./moti
20
20
  import { useChangeHighlight, flashChanged } from "./hooks/useChangeHighlight.js";
21
21
  import { useOptimisticValue } from "./hooks/useOptimisticValue.js";
22
22
  import { announcePolite } from "./announce.js";
23
+ import { useDeferredReorder } from "./hooks/useDeferredReorder.js";
24
+ import { useSlidingIndicator } from "./hooks/useSlidingIndicator.js";
25
+ import { useKeyboardGrab, settleDrop, DEFAULT_KEYBOARD_GRAB_LABELS } from "./hooks/useKeyboardGrab.js";
23
26
  import { computePosition } from "./overlay-position.js";
24
27
  import { Button, ButtonGroup, ButtonLink } from "./components/Button.js";
25
28
  import { UixLabelsProvider, useUixLabels } from "./labels-context.js";
@@ -125,7 +128,7 @@ import { Kbd, KbdCombo, ariaKeyShortcuts } from "./components/Kbd.js";
125
128
  import { Steps, Step, DEFAULT_STEP_STATE_LABELS } from "./components/Steps.js";
126
129
  import { Tooltip } from "./components/Tooltip.js";
127
130
  import { InfoTip } from "./components/InfoTip.js";
128
- import { EmptyState, ErrorState, ForbiddenState, NotFoundState, FilteredEmptyState, Skeleton, LoadingState } from "./components/States.js";
131
+ import { EmptyState, ErrorState, ForbiddenState, NotFoundState, FilteredEmptyState, Skeleton, LoadingState, SuccessState } from "./components/States.js";
129
132
  import { RelativeTime } from "./components/RelativeTime.js";
130
133
  import { parseDateValue, relativeTimeValue } from "./relative-time-model.js";
131
134
  import { AsyncOperationStatus } from "./components/AsyncOperationStatus.js";
@@ -136,6 +139,7 @@ import { SaveStatus, DEFAULT_SAVE_STATUS_LABELS } from "./components/SaveStatus.
136
139
  import { BusyRegion } from "./components/BusyRegion.js";
137
140
  import { ChangeHighlight } from "./components/ChangeHighlight.js";
138
141
  import { Count } from "./components/Count.js";
142
+ import { MotionList } from "./components/MotionList.js";
139
143
  import { DEFAULT_BUTTON_RESULT_LABELS } from "./components/ButtonFeedback.js";
140
144
  import { Composer, ComposerBar } from "./components/Composer.js";
141
145
  import { Segmented, SegmentedOption, LocaleSwitcher } from "./components/Segmented.js";
@@ -164,7 +168,7 @@ import { Menu, MenuItem, MenuItemRadio, MenuItemCheckbox, MenuRadioGroup, MenuGr
164
168
  import { CommandPalette, CommandGroup, CommandItem } from "./components/CommandPalette.js";
165
169
  import { filterCommandItems, groupCommandItems } from "./command-palette-model.js";
166
170
  import { Inbox, InboxList, InboxItem, InboxDetail } from "./components/Inbox.js";
167
- import { Kanban, KanbanColumn, KanbanCard } from "./components/Kanban.js";
171
+ import { Kanban, KanbanColumn, KanbanCard, KanbanDropLine } from "./components/Kanban.js";
168
172
  import { Tree, DEFAULT_TREE_LABELS } from "./components/Tree.js";
169
173
  import { OrgChart, DEFAULT_ORG_CHART_LABELS, orgChartInitials, orgChartZoomStep } from "./components/OrgChart.js";
170
174
  import { flattenTree, treeNav } from "./tree-model.js";
@@ -360,6 +364,7 @@ export {
360
364
  DEFAULT_FILTER_SUMMARY_LABELS,
361
365
  DEFAULT_INLINE_EDIT_LABELS,
362
366
  DEFAULT_INSIGHT_CARD_LABELS,
367
+ DEFAULT_KEYBOARD_GRAB_LABELS,
363
368
  DEFAULT_LIGHTBOX_LABELS,
364
369
  DEFAULT_MATCH_REVIEW_LABELS,
365
370
  DEFAULT_ORG_CHART_LABELS,
@@ -431,6 +436,7 @@ export {
431
436
  Kanban,
432
437
  KanbanCard,
433
438
  KanbanColumn,
439
+ KanbanDropLine,
434
440
  Kbd,
435
441
  KbdCombo,
436
442
  Label,
@@ -453,6 +459,7 @@ export {
453
459
  Meter,
454
460
  MetricInput,
455
461
  Modal,
462
+ MotionList,
456
463
  NUMBER_FILTER_OPERATORS,
457
464
  NavFavourites,
458
465
  NavGroup,
@@ -519,6 +526,7 @@ export {
519
526
  Step,
520
527
  Steps,
521
528
  SubNavItem,
529
+ SuccessState,
522
530
  Switch,
523
531
  TEXT_FILTER_OPERATORS,
524
532
  TOAST_LEAVE_MS,
@@ -677,6 +685,7 @@ export {
677
685
  selectRangeDate,
678
686
  separateEdgeLabels,
679
687
  setRuleCombinator,
688
+ settleDrop,
680
689
  shiftSpan,
681
690
  snapToStep,
682
691
  splitDateTime,
@@ -704,11 +713,14 @@ export {
704
713
  treeNav,
705
714
  useAnchoredPosition,
706
715
  useChangeHighlight,
716
+ useDeferredReorder,
707
717
  useDelayedPending,
708
718
  useDialog,
709
719
  useDisplayPreferences,
720
+ useKeyboardGrab,
710
721
  useOptimisticValue,
711
722
  useReducedMotion,
723
+ useSlidingIndicator,
712
724
  useTable,
713
725
  useUixLabels,
714
726
  useVirtualRows,
@@ -17,6 +17,7 @@ import { OrgChartLabels } from './components/OrgChart.js';
17
17
  import { InlineEditLabels } from './components/InlineEdit.js';
18
18
  import { DiscardPromptLabels } from './components/DiscardPrompt.js';
19
19
  import { SearchBarLabels } from './components/SearchBar.js';
20
+ import { KeyboardGrabLabels } from './hooks/useKeyboardGrab.js';
20
21
  import { LightboxLabels } from './components/Lightbox.js';
21
22
  import { SortableListLabels } from './components/SortableList.js';
22
23
  import { DiffViewerLabels } from './components/DiffViewer.js';
@@ -143,6 +144,8 @@ interface UixLabels {
143
144
  inlineEdit?: Partial<InlineEditLabels>;
144
145
  /** `SortableList` (HAR-1375): the grip, Move up / Move down, the move announcement. */
145
146
  sortableList?: Partial<SortableListLabels>;
147
+ /** `useKeyboardGrab` (HAR-2590): picked up, dropped, cancelled; `{label}`. */
148
+ keyboardGrab?: Partial<KeyboardGrabLabels>;
146
149
  /** `typeToConfirmMismatch` (HAR-1831): `{value}` is the text to type. */
147
150
  confirmDialog?: {
148
151
  typeToConfirm?: string;
@@ -0,0 +1,11 @@
1
+ declare const KEY_OPENED = "data-uix-key-opened";
2
+ /** Start following the input modality (idempotent; capture phase, so nothing can hide it). */
3
+ declare function watchInputModality(): void;
4
+ /** Was the input that led here a key press? `false` before any input and on the server. */
5
+ declare function keyboardInitiated(): boolean;
6
+ /** Mark `el` instant when the current open or close came from the keyboard; clear it otherwise. */
7
+ declare function markKeyOpened(el: Element | null | undefined): void;
8
+ /** A time token (`--uix-dur-exit`) in milliseconds, read from `el` (default: the root), or `fallback`. */
9
+ declare function tokenMs(name: string, fallback: number, el?: Element | null): number;
10
+
11
+ export { KEY_OPENED, keyboardInitiated, markKeyOpened, tokenMs, watchInputModality };
package/dist/motion.js ADDED
@@ -0,0 +1,35 @@
1
+ const KEY_OPENED = "data-uix-key-opened";
2
+ let keyboard = false;
3
+ let watching = false;
4
+ function watchInputModality() {
5
+ if (watching || typeof document === "undefined") return;
6
+ watching = true;
7
+ document.addEventListener("keydown", (event) => {
8
+ if (!["Shift", "Control", "Alt", "Meta"].includes(event.key)) keyboard = true;
9
+ }, true);
10
+ document.addEventListener("pointerdown", () => {
11
+ keyboard = false;
12
+ }, true);
13
+ }
14
+ function keyboardInitiated() {
15
+ return keyboard;
16
+ }
17
+ function markKeyOpened(el) {
18
+ if (!el) return;
19
+ if (keyboard) el.setAttribute(KEY_OPENED, "");
20
+ else el.removeAttribute(KEY_OPENED);
21
+ }
22
+ function tokenMs(name, fallback, el) {
23
+ if (typeof getComputedStyle === "undefined" || typeof document === "undefined") return fallback;
24
+ const raw = getComputedStyle(el ?? document.documentElement).getPropertyValue(name).trim();
25
+ const m = /^(-?\d*\.?\d+)(ms|s)$/.exec(raw);
26
+ if (!m) return fallback;
27
+ return m[2] === "s" ? Number(m[1]) * 1e3 : Number(m[1]);
28
+ }
29
+ export {
30
+ KEY_OPENED,
31
+ keyboardInitiated,
32
+ markKeyOpened,
33
+ tokenMs,
34
+ watchInputModality
35
+ };
@@ -137,6 +137,8 @@ interface RoutedEdge {
137
137
  bus: boolean;
138
138
  /** The label wrapped to fit the column gap (at most two lines). */
139
139
  labelLines: string[];
140
+ /** The widest chip the label may take at its place (map units): the column gap, or a bus stub. */
141
+ labelRoom: number;
140
142
  }
141
143
  interface LayeredColumn {
142
144
  depth: number;
@@ -187,6 +189,15 @@ interface EdgeLabelBox {
187
189
  y: number;
188
190
  width: number;
189
191
  height: number;
192
+ /** Placed first when labels compete for room (a toned, emphasised or one-of-a-kind label). */
193
+ priority?: number;
194
+ }
195
+ /** A box an edge label must not cover (a node), by its top-left corner. */
196
+ interface LabelObstacle {
197
+ x: number;
198
+ y: number;
199
+ width: number;
200
+ height: number;
190
201
  }
191
202
  /** Where a label is drawn after `separateEdgeLabels`, or that it is left out. */
192
203
  interface PlacedEdgeLabel {
@@ -194,7 +205,7 @@ interface PlacedEdgeLabel {
194
205
  y: number;
195
206
  hidden: boolean;
196
207
  }
197
- /** The box a wrapped edge label takes, from its lines (the meta type size, as `wrapEdgeLabel` assumes). */
208
+ /** The chip a wrapped edge label takes, from its lines (the meta type size, as `wrapEdgeLabel` assumes). */
198
209
  declare function edgeLabelSize(lines: readonly string[]): {
199
210
  width: number;
200
211
  height: number;
@@ -204,14 +215,17 @@ declare function edgeLabelSize(lines: readonly string[]): {
204
215
  * of each other in a dense layered column). Labels are taken top to bottom; one that overlaps a
205
216
  * label already placed moves down just below it. A label that would have to move further than
206
217
  * `maxShift` from its edge is left out instead (the relationship is still in the nodes'
207
- * descriptions and in the list). Deterministic: the same boxes give the same placement.
218
+ * descriptions and in the list), and so is one that would cover an `obstacle` (a node). Labels with
219
+ * a higher `priority` are placed first, so a repeated relation gives way to a one-of-a-kind or
220
+ * toned one. Deterministic: the same boxes give the same placement.
208
221
  */
209
222
  declare function separateEdgeLabels(boxes: readonly EdgeLabelBox[], options?: {
210
223
  gap?: number;
211
224
  maxShift?: number;
225
+ obstacles?: readonly LabelObstacle[];
212
226
  }): Map<string, PlacedEdgeLabel>;
213
227
  type LayeredNavigationKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End';
214
228
  /** Keyboard traversal over a layered layout: ←/→ along forward edges, ↑/↓ within a column. */
215
229
  declare function layeredNeighbor(layout: LayeredLayout, currentId: string | undefined, key: LayeredNavigationKey): string | undefined;
216
230
 
217
- export { type BoundedRelationshipGraph, type EdgeLabelBox, type LayeredColumn, type LayeredEdgeClassification, type LayeredEdgeKind, type LayeredItem, type LayeredLayout, type LayeredLayoutOptions, type LayeredNavigationKey, type PlacedEdgeLabel, type PositionedRelationshipNode, type RelationshipGraphArrow, type RelationshipGraphCluster, type RelationshipGraphEdge, type RelationshipGraphNode, type RelationshipGraphTone, type RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, edgeLabelSize, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, separateEdgeLabels, traverseRelationshipNode, wrapEdgeLabel };
231
+ export { type BoundedRelationshipGraph, type EdgeLabelBox, type LabelObstacle, type LayeredColumn, type LayeredEdgeClassification, type LayeredEdgeKind, type LayeredItem, type LayeredLayout, type LayeredLayoutOptions, type LayeredNavigationKey, type PlacedEdgeLabel, type PositionedRelationshipNode, type RelationshipGraphArrow, type RelationshipGraphCluster, type RelationshipGraphEdge, type RelationshipGraphNode, type RelationshipGraphTone, type RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, edgeLabelSize, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, separateEdgeLabels, traverseRelationshipNode, wrapEdgeLabel };
@@ -68,30 +68,37 @@ const LAYERED_PADDING = 16;
68
68
  const LAYERED_HEADER = 28;
69
69
  const GROUP_HEADER = 28;
70
70
  const AUTO_LABEL_MAX_FANOUT = 4;
71
- const LABEL_CHAR_WIDTH = 5.6;
71
+ const LABEL_CHAR_WIDTH = 6.3;
72
+ const LABEL_PAD_X = 6;
73
+ const LABEL_PAD_Y = 3;
74
+ const LABEL_LINE = 15;
75
+ const BUS_INSET = 4;
76
+ const ARROW_LENGTH = 9;
72
77
  const LABEL_MAX_LINES = 2;
73
78
  function wrapEdgeLabel(label, width) {
74
79
  const max = Math.max(4, Math.floor(width / LABEL_CHAR_WIDTH));
75
80
  const clip = (word) => word.length > max ? `${word.slice(0, max - 1)}\u2026` : word;
81
+ const words = label.split(/\s+/).filter(Boolean);
76
82
  const lines = [];
77
83
  let current = "";
78
- for (const word of label.split(/\s+/).filter(Boolean)) {
84
+ let placed = 0;
85
+ for (const word of words) {
79
86
  const next = current ? `${current} ${word}` : word;
80
87
  if (next.length <= max) {
81
88
  current = next;
89
+ placed += 1;
82
90
  continue;
83
91
  }
84
92
  if (current) lines.push(current);
85
- current = clip(word);
93
+ current = "";
86
94
  if (lines.length === LABEL_MAX_LINES) break;
95
+ current = clip(word);
96
+ placed += 1;
87
97
  }
88
98
  if (current && lines.length < LABEL_MAX_LINES) lines.push(current);
89
- if (lines.length === LABEL_MAX_LINES) {
90
- const consumed = lines.join(" ").replace(/…$/, "").length;
91
- if (consumed < label.replace(/\s+/g, " ").trim().length) {
92
- const last = lines[LABEL_MAX_LINES - 1];
93
- lines[LABEL_MAX_LINES - 1] = last.endsWith("\u2026") ? last : `${last.slice(0, Math.max(1, max - 1))}\u2026`;
94
- }
99
+ if (placed < words.length && lines.length > 0) {
100
+ const last = lines[lines.length - 1];
101
+ if (!last.endsWith("\u2026")) lines[lines.length - 1] = last.length < max ? `${last}\u2026` : `${last.slice(0, max - 1)}\u2026`;
95
102
  }
96
103
  return lines;
97
104
  }
@@ -403,9 +410,9 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
403
410
  startY = from.y + from.height / 2;
404
411
  endX = to.x;
405
412
  endY = to.y + to.height / 2;
406
- const trunk = startX + columnGap / 2;
413
+ const trunk = startX + BUS_INSET;
407
414
  path = busPath(startX, startY, trunk, endX, endY);
408
- labelX = (trunk + endX) / 2;
415
+ labelX = (trunk + BUS_INSET + endX - ARROW_LENGTH) / 2;
409
416
  labelY = endY;
410
417
  startAngle = Math.PI;
411
418
  endAngle = 0;
@@ -463,6 +470,7 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
463
470
  arrowY: round1(atStart ? startY : endY),
464
471
  arrowAngle: atStart ? startAngle : endAngle,
465
472
  labelLines: [],
473
+ labelRoom: columnGap - 8,
466
474
  columnLabel: false,
467
475
  bus: kind === "forward" && from.children.length > AUTO_LABEL_MAX_FANOUT,
468
476
  autoLabel: to.kind !== "cluster" && (edge.emphasis === true || kind === "forward" && from.children.length <= AUTO_LABEL_MAX_FANOUT)
@@ -508,7 +516,9 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
508
516
  const seenLabels = /* @__PURE__ */ new Set();
509
517
  for (const entry of routed.values()) {
510
518
  if (entry.label === void 0) continue;
511
- entry.labelLines = wrapEdgeLabel(entry.label, columnGap - 8);
519
+ const run = entry.bus ? columnGap - 2 * BUS_INSET - ARROW_LENGTH : columnGap - 8;
520
+ entry.labelRoom = run;
521
+ entry.labelLines = wrapEdgeLabel(entry.label, run - 2 * LABEL_PAD_X);
512
522
  if (!entry.autoLabel || entry.emphasis || entry.kind !== "forward") continue;
513
523
  const key = `${entry.source}|${entry.label}`;
514
524
  if (seenLabels.has(key)) entry.autoLabel = false;
@@ -530,14 +540,15 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
530
540
  }
531
541
  function edgeLabelSize(lines) {
532
542
  const longest = lines.reduce((max, line) => Math.max(max, line.length), 0);
533
- return { width: longest * LABEL_CHAR_WIDTH + 8, height: Math.max(1, lines.length) * 13 + 2 };
543
+ return { width: longest * LABEL_CHAR_WIDTH + 2 * LABEL_PAD_X, height: Math.max(1, lines.length) * LABEL_LINE + 2 * LABEL_PAD_Y };
534
544
  }
535
545
  function separateEdgeLabels(boxes, options = {}) {
536
546
  const gap = options.gap ?? 2;
537
- const maxShift = options.maxShift ?? 40;
547
+ const maxShift = options.maxShift ?? 48;
538
548
  const placed = [];
539
549
  const result = /* @__PURE__ */ new Map();
540
- const ordered = [...boxes].sort((a, b) => a.y - b.y || a.x - b.x || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
550
+ const obstacles = options.obstacles ?? [];
551
+ const ordered = [...boxes].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0) || a.y - b.y || a.x - b.x || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
541
552
  for (const box of ordered) {
542
553
  const left = box.x - box.width / 2;
543
554
  const right = box.x + box.width / 2;
@@ -548,7 +559,8 @@ function separateEdgeLabels(boxes, options = {}) {
548
559
  top = hit.bottom + gap;
549
560
  }
550
561
  const shift = top - (box.y - box.height / 2);
551
- if (shift > maxShift) {
562
+ const covers = obstacles.some((o) => left < o.x + o.width && o.x < right && top < o.y + o.height && o.y < top + box.height);
563
+ if (shift > maxShift || covers) {
552
564
  result.set(box.id, { x: box.x, y: box.y, hidden: true });
553
565
  continue;
554
566
  }
@@ -1369,12 +1369,17 @@ function removeAnchorName(el, name) {
1369
1369
  else el.style.removeProperty("anchor-name");
1370
1370
  }
1371
1371
  function translationOf(el) {
1372
- const t = getComputedStyle(el).transform;
1372
+ const style = getComputedStyle(el);
1373
+ const t = style.transform;
1373
1374
  if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
1374
1375
  try {
1375
1376
  const m = new DOMMatrixReadOnly(t);
1377
+ const [ox = 0, oy = 0] = style.transformOrigin.split(" ").map((v) => parseFloat(v) || 0);
1378
+ const cx2 = el.offsetWidth / 2 - ox;
1379
+ const cy = el.offsetHeight / 2 - oy;
1380
+ const p = m.transformPoint({ x: cx2, y: cy });
1376
1381
  const z = zoomOf(el);
1377
- return { x: m.m41 * z, y: m.m42 * z };
1382
+ return { x: (p.x - cx2) * z, y: (p.y - cy) * z };
1378
1383
  } catch {
1379
1384
  return { x: 0, y: 0 };
1380
1385
  }
@@ -1471,6 +1476,13 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
1471
1476
  const measured = size.width > 0 || size.height > 0;
1472
1477
  if (measured && session.current) side.current = pos.side;
1473
1478
  if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
1479
+ if (measured) {
1480
+ const clamp2 = (v, max) => Math.min(Math.max(v, 0), max);
1481
+ const ox = pos.side === "left" ? size.width : pos.side === "right" ? 0 : clamp2(a.x + a.width / 2 - pos.x, size.width);
1482
+ const oy = pos.side === "top" ? size.height : pos.side === "bottom" ? 0 : clamp2(a.y + a.height / 2 - pos.y, size.height);
1483
+ const origin = `${Math.round(ox / zoom)}px ${Math.round(oy / zoom)}px`;
1484
+ if (s.getPropertyValue("--uix-anchor-origin") !== origin) s.setProperty("--uix-anchor-origin", origin);
1485
+ }
1474
1486
  const writeFixed = (at) => {
1475
1487
  const left2 = `${Math.round(at.x / zoom)}px`;
1476
1488
  const top2 = `${Math.round(at.y / zoom)}px`;
@@ -1567,6 +1579,26 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
1567
1579
  return reposition;
1568
1580
  }
1569
1581
 
1582
+ // src/motion.ts
1583
+ var KEY_OPENED = "data-uix-key-opened";
1584
+ var keyboard = false;
1585
+ var watching = false;
1586
+ function watchInputModality() {
1587
+ if (watching || typeof document === "undefined") return;
1588
+ watching = true;
1589
+ document.addEventListener("keydown", (event) => {
1590
+ if (!["Shift", "Control", "Alt", "Meta"].includes(event.key)) keyboard = true;
1591
+ }, true);
1592
+ document.addEventListener("pointerdown", () => {
1593
+ keyboard = false;
1594
+ }, true);
1595
+ }
1596
+ function markKeyOpened(el) {
1597
+ if (!el) return;
1598
+ if (keyboard) el.setAttribute(KEY_OPENED, "");
1599
+ else el.removeAttribute(KEY_OPENED);
1600
+ }
1601
+
1570
1602
  // src/components/Popover.tsx
1571
1603
  var import_jsx_runtime4 = require("react/jsx-runtime");
1572
1604
  var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
@@ -1620,6 +1652,14 @@ function Popover({
1620
1652
  el.hidePopover();
1621
1653
  } : void 0
1622
1654
  });
1655
+ (0, import_react4.useEffect)(() => {
1656
+ watchInputModality();
1657
+ const el = ref.current;
1658
+ if (!el) return;
1659
+ const mark = () => markKeyOpened(el);
1660
+ el.addEventListener("beforetoggle", mark);
1661
+ return () => el.removeEventListener("beforetoggle", mark);
1662
+ }, []);
1623
1663
  (0, import_react4.useEffect)(() => {
1624
1664
  const el = ref.current;
1625
1665
  if (!el || !anchor) return;
@@ -74,8 +74,11 @@ interface ToastStore {
74
74
  subscribe: (listener: () => void) => () => void;
75
75
  getSnapshot: () => readonly ToastRecord[];
76
76
  }
77
- /** How long the leave animation gets before the record is removed. */
78
- declare const TOAST_LEAVE_MS = 200;
77
+ /**
78
+ * How long the leave transition gets before the record is removed when no stylesheet answers:
79
+ * the store reads `--uix-dur-exit` (toast.css leaves on it), so the two never disagree (HAR-2578).
80
+ */
81
+ declare const TOAST_LEAVE_MS = 110;
79
82
  /** A separate queue, e.g. per test or per embedded app. Most products use the default `toast`. */
80
83
  declare function createToastStore(): ToastStore;
81
84
  interface ToastApi {
@@ -1,3 +1,4 @@
1
+ import { tokenMs } from "./motion.js";
1
2
  const DEFAULT_DURATION = {
2
3
  success: 5e3,
3
4
  info: 5e3,
@@ -6,7 +7,7 @@ const DEFAULT_DURATION = {
6
7
  error: null,
7
8
  loading: null
8
9
  };
9
- const TOAST_LEAVE_MS = 200;
10
+ const TOAST_LEAVE_MS = 110;
10
11
  let counter = 0;
11
12
  function createToastStore() {
12
13
  let records = [];
@@ -52,7 +53,7 @@ function createToastStore() {
52
53
  set(records.map((r) => ids.has(r.id) ? { ...r, leaving: true } : r));
53
54
  for (const r of closing) r.onDismiss?.(reason);
54
55
  const remove = () => set(records.filter((r) => !(ids.has(r.id) && r.leaving)));
55
- if (typeof setTimeout === "function") setTimeout(remove, TOAST_LEAVE_MS);
56
+ if (typeof setTimeout === "function") setTimeout(remove, tokenMs("--uix-dur-exit", TOAST_LEAVE_MS));
56
57
  else remove();
57
58
  };
58
59
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/react",
3
- "version": "2.48.0",
3
+ "version": "2.49.0",
4
4
  "description": "Thin React wrappers over the UIx v2 CSS component library.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",