@trackunit/react-components 1.15.21 → 1.15.23

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.
package/index.esm.js CHANGED
@@ -7571,6 +7571,406 @@ const useIsFullscreen = () => {
7571
7571
  return isFullScreen;
7572
7572
  };
7573
7573
 
7574
+ /**
7575
+ * Detects the current platform based on the user agent.
7576
+ *
7577
+ * @returns {Platform} The detected platform: "mac", "windows", or "linux"
7578
+ */
7579
+ const getPlatform = () => {
7580
+ if (typeof navigator === "undefined") {
7581
+ // SSR default - most users are on Windows
7582
+ return "windows";
7583
+ }
7584
+ const ua = navigator.userAgent.toLowerCase();
7585
+ if (ua.includes("mac")) {
7586
+ return "mac";
7587
+ }
7588
+ if (ua.includes("linux")) {
7589
+ return "linux";
7590
+ }
7591
+ return "windows";
7592
+ };
7593
+
7594
+ /** Display order for modifiers: mod first, then alt, then shift */
7595
+ const MODIFIER_DISPLAY_ORDER = ["mod", "alt", "shift"];
7596
+ /** Labels for each modifier, mapped per platform */
7597
+ const MODIFIER_LABELS = {
7598
+ mod: { mac: "Cmd", other: "Ctrl" },
7599
+ alt: { mac: "Option", other: "Alt" },
7600
+ shift: { mac: "Shift", other: "Shift" },
7601
+ };
7602
+ /**
7603
+ * Maps a semantic modifier to a display label based on the current platform.
7604
+ */
7605
+ const getModifierLabel = (mod) => {
7606
+ const labels = MODIFIER_LABELS[mod];
7607
+ return getPlatform() === "mac" ? labels.mac : labels.other;
7608
+ };
7609
+ /**
7610
+ * Maps a key to a more readable display label.
7611
+ */
7612
+ const getKeyLabel = (key) => {
7613
+ const keyLabels = {
7614
+ arrowup: "↑",
7615
+ arrowdown: "↓",
7616
+ arrowleft: "←",
7617
+ arrowright: "→",
7618
+ enter: "Enter",
7619
+ escape: "Esc",
7620
+ tab: "Tab",
7621
+ space: "Space",
7622
+ backspace: "Backspace",
7623
+ delete: "Delete",
7624
+ };
7625
+ const lowerKey = key.toLowerCase();
7626
+ return keyLabels[lowerKey] ?? key.toUpperCase();
7627
+ };
7628
+ /**
7629
+ * Formats a shortcut definition into a human-readable label.
7630
+ *
7631
+ * The label is platform-aware:
7632
+ * - Mac: "Cmd + K", "Option + Shift + L"
7633
+ * - Windows/Linux: "Ctrl + K", "Alt + Shift + L"
7634
+ *
7635
+ * @param shortcut - The shortcut definition to format
7636
+ * @param separator - The separator between parts (default: " + ")
7637
+ * @returns {string} A formatted string like "Cmd + K" or "Ctrl + Shift + L"
7638
+ * @example
7639
+ * formatShortcutLabel({ key: "k", modifiers: "mod" })
7640
+ * // Mac: "Cmd + K"
7641
+ * // Windows: "Ctrl + K"
7642
+ * @example
7643
+ * formatShortcutLabel({ key: "l", modifiers: "mod+shift" })
7644
+ * // Mac: "Cmd + Shift + L"
7645
+ * // Windows: "Ctrl + Shift + L"
7646
+ * @example
7647
+ * formatShortcutLabel({ key: "Escape" })
7648
+ * // "Esc"
7649
+ */
7650
+ const formatShortcutLabel = (shortcut, separator = " + ") => {
7651
+ // Parse modifier string and sort in display order (mod, alt, shift)
7652
+ const modifierSet = new Set(shortcut.modifiers?.split("+") ?? []);
7653
+ const sortedModifiers = MODIFIER_DISPLAY_ORDER.filter(mod => modifierSet.has(mod));
7654
+ const modLabels = sortedModifiers.map(getModifierLabel);
7655
+ const keyLabel = getKeyLabel(shortcut.key);
7656
+ return [...modLabels, keyLabel].join(separator);
7657
+ };
7658
+
7659
+ /**
7660
+ * Parses a modifier string into a Set for easy lookup.
7661
+ *
7662
+ * @param modifiers - The modifier string (e.g., "mod+shift")
7663
+ * @returns {Set<string>} A Set containing the individual modifiers
7664
+ */
7665
+ const parseModifiers = (modifiers) => {
7666
+ if (!modifiers) {
7667
+ return new Set();
7668
+ }
7669
+ return new Set(modifiers.split("+"));
7670
+ };
7671
+ /**
7672
+ * Checks if a keyboard event matches the shortcut definition.
7673
+ *
7674
+ * This is a pure function that handles:
7675
+ * - Case-insensitive key matching
7676
+ * - Platform-aware modifier key checking (Cmd on Mac, Ctrl on Windows/Linux)
7677
+ * - Space key normalization
7678
+ * - Extra modifier rejection (ensures only expected modifiers are pressed)
7679
+ *
7680
+ * @param event - The keyboard event to check
7681
+ * @param shortcut - The shortcut definition to match against
7682
+ * @returns {boolean} true if the event matches the shortcut, false otherwise
7683
+ * @example
7684
+ * // Check if Cmd+K (Mac) or Ctrl+K (Windows) was pressed
7685
+ * const isMatch = matchesShortcut(event, { key: "k", modifiers: "mod" });
7686
+ */
7687
+ const matchesShortcut = (event, shortcut) => {
7688
+ // Check the primary key (case-insensitive)
7689
+ const eventKey = event.key.toLowerCase();
7690
+ const shortcutKey = shortcut.key.toLowerCase();
7691
+ // Handle special keys that might have different representations
7692
+ // Normalize both to the same representation for comparison
7693
+ const normalizedEventKey = eventKey === " " ? "space" : eventKey;
7694
+ const normalizedShortcutKey = shortcutKey === " " ? "space" : shortcutKey;
7695
+ if (normalizedEventKey !== normalizedShortcutKey) {
7696
+ return false;
7697
+ }
7698
+ const modifiers = parseModifiers(shortcut.modifiers);
7699
+ // Check "mod" modifier (Cmd on Mac, Ctrl on Windows/Linux)
7700
+ const expectMod = modifiers.has("mod");
7701
+ const hasMod = getPlatform() === "mac" ? event.metaKey : event.ctrlKey;
7702
+ if (expectMod !== hasMod) {
7703
+ return false;
7704
+ }
7705
+ // Check "alt" modifier (Option on Mac, Alt on Windows/Linux)
7706
+ const expectAlt = modifiers.has("alt");
7707
+ if (expectAlt !== event.altKey) {
7708
+ return false;
7709
+ }
7710
+ // Check "shift" modifier
7711
+ const expectShift = modifiers.has("shift");
7712
+ if (expectShift !== event.shiftKey) {
7713
+ return false;
7714
+ }
7715
+ // Make sure no extra modifiers are pressed
7716
+ // If mod is expected, we've already checked the right key (meta or ctrl)
7717
+ // But we need to ensure the OTHER mod key isn't also pressed
7718
+ if (!expectMod) {
7719
+ // If we don't expect mod, neither metaKey nor ctrlKey should be pressed
7720
+ if (event.metaKey || event.ctrlKey) {
7721
+ return false;
7722
+ }
7723
+ }
7724
+ else {
7725
+ // If we expect mod, the opposite mod key shouldn't be pressed
7726
+ const oppositeModPressed = getPlatform() === "mac" ? event.ctrlKey : event.metaKey;
7727
+ if (oppositeModPressed) {
7728
+ return false;
7729
+ }
7730
+ }
7731
+ return true;
7732
+ };
7733
+
7734
+ /**
7735
+ * Reserved application shortcuts.
7736
+ * These are host-level shortcuts that are blocked at the type level.
7737
+ * See reservedShortcutTypes.ts for the type-level enforcement.
7738
+ *
7739
+ * IMPORTANT: When adding a new reserved shortcut here, you must also
7740
+ * update the type definitions in reservedShortcutTypes.ts and the
7741
+ * isGlobalShortcut() function in apps/iris-app-loader/src/forward-events-to-host.ts.
7742
+ */
7743
+ const RESERVED_SHORTCUTS = {
7744
+ "mod+k": {
7745
+ name: "Global Search",
7746
+ owner: "host-navigation",
7747
+ },
7748
+ "mod+shift+l": {
7749
+ name: "Local Dev Mode Toggle",
7750
+ owner: "host-navigation",
7751
+ },
7752
+ };
7753
+ /**
7754
+ * Common browser shortcuts that cannot be reliably overridden.
7755
+ * Using these shortcuts will trigger a development warning since
7756
+ * the browser typically intercepts them before JavaScript can handle them.
7757
+ *
7758
+ * Note: Some of these can be overridden with preventDefault(), but it's
7759
+ * generally a bad UX to override expected browser behavior.
7760
+ */
7761
+ const BROWSER_SHORTCUTS = {
7762
+ // Tab management
7763
+ "mod+t": "New tab",
7764
+ "mod+w": "Close tab",
7765
+ "mod+shift+t": "Reopen closed tab",
7766
+ "mod+n": "New window",
7767
+ "mod+shift+n": "New incognito window",
7768
+ // Navigation
7769
+ "mod+l": "Focus address bar",
7770
+ "mod+r": "Reload page",
7771
+ "mod+shift+r": "Hard reload",
7772
+ // Page actions
7773
+ "mod+s": "Save page",
7774
+ "mod+p": "Print",
7775
+ "mod+f": "Find in page",
7776
+ "mod+g": "Find next",
7777
+ "mod+shift+g": "Find previous",
7778
+ "mod+o": "Open file",
7779
+ // Browser features
7780
+ "mod+h": "History (Chrome) / Hide window (Mac)",
7781
+ "mod+j": "Downloads",
7782
+ "mod+d": "Bookmark page",
7783
+ "mod+shift+b": "Toggle bookmarks bar",
7784
+ // Developer tools
7785
+ "mod+shift+i": "Developer tools",
7786
+ "mod+shift+j": "JavaScript console",
7787
+ "mod+shift+c": "Inspect element",
7788
+ // Function keys
7789
+ f1: "Help",
7790
+ f3: "Find next",
7791
+ f5: "Reload",
7792
+ f11: "Fullscreen",
7793
+ f12: "Developer tools",
7794
+ // Zoom
7795
+ "mod+0": "Reset zoom",
7796
+ "mod+-": "Zoom out",
7797
+ "mod+=": "Zoom in",
7798
+ };
7799
+
7800
+ /**
7801
+ * Converts a ShortcutDefinition to a normalized string key for lookup.
7802
+ * Format: "mod+shift+k" (modifiers sorted alphabetically, then key)
7803
+ */
7804
+ const shortcutToString = (shortcut) => {
7805
+ if (!shortcut.modifiers) {
7806
+ return shortcut.key.toLowerCase();
7807
+ }
7808
+ // Modifiers are already in alphabetical order (enforced by type)
7809
+ return `${shortcut.modifiers}+${shortcut.key.toLowerCase()}`;
7810
+ };
7811
+
7812
+ /**
7813
+ * Hook for checking keyboard shortcut conflicts.
7814
+ *
7815
+ * This hook checks if a shortcut conflicts with:
7816
+ * - Reserved application shortcuts (host-level features)
7817
+ * - Browser shortcuts (that cannot be reliably overridden)
7818
+ *
7819
+ * In development mode, it logs a warning if the shortcut conflicts with
7820
+ * browser shortcuts. Reserved shortcuts are blocked at the type level.
7821
+ *
7822
+ * @param shortcut - The shortcut definition to check
7823
+ * @param options - Optional configuration
7824
+ * @returns {ShortcutConflictInfo} Conflict information for the shortcut
7825
+ * @example
7826
+ * const conflicts = useShortcutConflicts({ key: "k", modifiers: "mod" });
7827
+ * if (conflicts.isReserved) {
7828
+ * console.log(`Reserved by: ${conflicts.reservedInfo?.owner}`);
7829
+ * }
7830
+ */
7831
+ const useShortcutConflicts = (shortcut, options) => {
7832
+ const disabled = options?.disabled ?? false;
7833
+ const conflictInfo = useMemo(() => {
7834
+ if (disabled) {
7835
+ return {
7836
+ isReserved: false,
7837
+ reservedInfo: undefined,
7838
+ browserConflict: undefined,
7839
+ };
7840
+ }
7841
+ const key = shortcutToString(shortcut);
7842
+ const reservedInfo = RESERVED_SHORTCUTS[key];
7843
+ const browserConflict = BROWSER_SHORTCUTS[key];
7844
+ return {
7845
+ isReserved: reservedInfo !== undefined,
7846
+ reservedInfo,
7847
+ browserConflict,
7848
+ };
7849
+ }, [shortcut, disabled]);
7850
+ // Log development warning for browser conflicts
7851
+ useEffect(() => {
7852
+ if (disabled || process.env.NODE_ENV === "production") {
7853
+ return;
7854
+ }
7855
+ if (conflictInfo.browserConflict) {
7856
+ const shortcutStr = shortcutToString(shortcut);
7857
+ // eslint-disable-next-line no-console -- Development warning for browser shortcut conflicts
7858
+ console.warn(`[useKeyboardShortcut] Shortcut "${shortcutStr}" conflicts with browser shortcut: "${conflictInfo.browserConflict}". ` +
7859
+ `This shortcut may not work reliably as the browser may intercept it.`);
7860
+ }
7861
+ // Only run on mount
7862
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7863
+ }, []);
7864
+ return conflictInfo;
7865
+ };
7866
+
7867
+ /** Time window in ms for the browser fallback escape hatch. */
7868
+ const REPEAT_WINDOW_MS = 400;
7869
+ /**
7870
+ * Hook for handling keyboard shortcuts with platform-aware modifiers.
7871
+ *
7872
+ * Reserved shortcuts are blocked at the type level and will show specific error messages.
7873
+ * There are two categories:
7874
+ *
7875
+ * **App-reserved** (host-level features):
7876
+ * - mod+k: Global Search
7877
+ * - mod+shift+l: Local Dev Mode Toggle
7878
+ *
7879
+ * **Browser-reserved** (native browser shortcuts):
7880
+ * - Clipboard: mod+c (Copy), mod+v (Paste), mod+x (Cut), mod+a (Select All)
7881
+ * - Undo/Redo: mod+z (Undo), mod+shift+z (Redo Mac), mod+y (Redo Win/Linux)
7882
+ * - Find: mod+f (Find), mod+g (Find next), mod+shift+g (Find previous)
7883
+ * - Navigation: mod+r (Reload), mod+shift+r (Hard reload), mod+t (New tab),
7884
+ * mod+w (Close tab), mod+shift+t (Reopen tab), mod+n (New window),
7885
+ * mod+shift+n (Incognito), mod+l (Address bar), mod+d (Bookmark)
7886
+ * - Zoom: mod+= (Zoom in), mod+- (Zoom out), mod+0 (Reset zoom)
7887
+ * - Other: mod+p (Print), mod+s (Save), mod+o (Open), mod+h (History/Hide),
7888
+ * mod+j (Downloads), mod+q (Quit Mac)
7889
+ *
7890
+ * See reservedShortcutTypes.ts for the full list with error messages.
7891
+ *
7892
+ * @param shortcut - The shortcut definition with key and optional modifiers
7893
+ * @param options - Configuration options for the shortcut handler
7894
+ * @param options.onTrigger - Callback invoked when the shortcut is triggered
7895
+ * @param options.disabled - Disable the shortcut conditionally (default: false)
7896
+ * @param options.preventDefault - Controls browser default behavior:
7897
+ * - `"never"` (default) - Browser handles the event normally
7898
+ * - `"once"` - Prevent default, but rapid repeat (within 400ms) passes to browser
7899
+ * - `"always"` - Always prevent default, no browser escape hatch
7900
+ * @param options.target - Target element for the listener (default: "window")
7901
+ * @returns {{ label: string }} Object containing the formatted shortcut label (e.g., "Cmd + K" on Mac)
7902
+ * @example
7903
+ * // Simple Escape key handler (no preventDefault)
7904
+ * const { label } = useKeyboardShortcut(
7905
+ * { key: "Escape" },
7906
+ * { onTrigger: () => closeModal() }
7907
+ * );
7908
+ * // label = "Esc"
7909
+ * @example
7910
+ * // Always prevent default, allow rapid re-triggering
7911
+ * const { label } = useKeyboardShortcut(
7912
+ * { key: "m", modifiers: "mod" },
7913
+ * {
7914
+ * onTrigger: () => createNewItem(),
7915
+ * preventDefault: "always",
7916
+ * }
7917
+ * );
7918
+ * // label = "Cmd + M" (Mac) or "Ctrl + M" (Windows/Linux)
7919
+ */
7920
+ const useKeyboardShortcut = (shortcut, { onTrigger, disabled = false, preventDefault = "never", target = "window" }) => {
7921
+ // Use refs to avoid recreating the handler on every render
7922
+ const onTriggerRef = useRef(onTrigger);
7923
+ const preventDefaultRef = useRef(preventDefault);
7924
+ // Track when the shortcut was last triggered for browser fallback
7925
+ const lastTriggerRef = useRef(0);
7926
+ // Keep refs up to date
7927
+ useEffect(() => {
7928
+ onTriggerRef.current = onTrigger;
7929
+ preventDefaultRef.current = preventDefault;
7930
+ }, [onTrigger, preventDefault]);
7931
+ // Check for conflicts and warn in development (handled internally by the hook)
7932
+ useShortcutConflicts(shortcut, { disabled });
7933
+ const handleKeyDown = useCallback((event) => {
7934
+ if (matchesShortcut(event, shortcut)) {
7935
+ const now = Date.now();
7936
+ const timeSinceLastTrigger = now - lastTriggerRef.current;
7937
+ const mode = preventDefaultRef.current;
7938
+ // Browser fallback: "once" mode skips handling if pressed within the repeat window
7939
+ if (mode === "once" && timeSinceLastTrigger < REPEAT_WINDOW_MS) {
7940
+ return;
7941
+ }
7942
+ if (mode !== "never") {
7943
+ event.preventDefault();
7944
+ }
7945
+ lastTriggerRef.current = now;
7946
+ onTriggerRef.current();
7947
+ }
7948
+ }, [shortcut]);
7949
+ useEffect(() => {
7950
+ if (disabled) {
7951
+ return;
7952
+ }
7953
+ const targetElement = target === "window" ? window : target.current;
7954
+ if (!targetElement) {
7955
+ return;
7956
+ }
7957
+ // Use a wrapper that properly handles the event type for both Window and HTMLElement
7958
+ const keydownHandler = (e) => {
7959
+ if (e instanceof KeyboardEvent) {
7960
+ handleKeyDown(e);
7961
+ }
7962
+ };
7963
+ targetElement.addEventListener("keydown", keydownHandler);
7964
+ return () => {
7965
+ targetElement.removeEventListener("keydown", keydownHandler);
7966
+ };
7967
+ }, [disabled, target, handleKeyDown]);
7968
+ // Compute the human-readable label for the shortcut
7969
+ const label = useMemo(() => formatShortcutLabel(shortcut), [shortcut]);
7970
+ // Memoize the return object for referential stability
7971
+ return useMemo(() => ({ label }), [label]);
7972
+ };
7973
+
7574
7974
  /**
7575
7975
  * Hook that returns true if any modifier key (Ctrl, Alt, Shift, Meta/Cmd) is pressed
7576
7976
  *
@@ -7939,4 +8339,4 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
7939
8339
  return useMemo(() => ({ focused }), [focused]);
7940
8340
  };
7941
8341
 
7942
- export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMergeRefs, useModifierKey, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSelfUpdatingRef, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
8342
+ export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useKeyboardShortcut, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMergeRefs, useModifierKey, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSelfUpdatingRef, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "1.15.21",
3
+ "version": "1.15.23",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -14,10 +14,10 @@
14
14
  "@floating-ui/react": "^0.26.25",
15
15
  "string-ts": "^2.0.0",
16
16
  "tailwind-merge": "^2.0.0",
17
- "@trackunit/ui-design-tokens": "1.11.17",
18
- "@trackunit/css-class-variance-utilities": "1.11.17",
19
- "@trackunit/shared-utils": "1.13.17",
20
- "@trackunit/ui-icons": "1.11.16",
17
+ "@trackunit/ui-design-tokens": "1.11.18",
18
+ "@trackunit/css-class-variance-utilities": "1.11.18",
19
+ "@trackunit/shared-utils": "1.13.18",
20
+ "@trackunit/ui-icons": "1.11.17",
21
21
  "@tanstack/react-router": "1.114.29",
22
22
  "es-toolkit": "^1.39.10",
23
23
  "@tanstack/react-virtual": "3.13.12",
@@ -0,0 +1,24 @@
1
+ import { ShortcutDefinition } from "./types";
2
+ /**
3
+ * Formats a shortcut definition into a human-readable label.
4
+ *
5
+ * The label is platform-aware:
6
+ * - Mac: "Cmd + K", "Option + Shift + L"
7
+ * - Windows/Linux: "Ctrl + K", "Alt + Shift + L"
8
+ *
9
+ * @param shortcut - The shortcut definition to format
10
+ * @param separator - The separator between parts (default: " + ")
11
+ * @returns {string} A formatted string like "Cmd + K" or "Ctrl + Shift + L"
12
+ * @example
13
+ * formatShortcutLabel({ key: "k", modifiers: "mod" })
14
+ * // Mac: "Cmd + K"
15
+ * // Windows: "Ctrl + K"
16
+ * @example
17
+ * formatShortcutLabel({ key: "l", modifiers: "mod+shift" })
18
+ * // Mac: "Cmd + Shift + L"
19
+ * // Windows: "Ctrl + Shift + L"
20
+ * @example
21
+ * formatShortcutLabel({ key: "Escape" })
22
+ * // "Esc"
23
+ */
24
+ export declare const formatShortcutLabel: (shortcut: ShortcutDefinition, separator?: string) => string;
@@ -0,0 +1,20 @@
1
+ import { Platform } from "./types";
2
+ /**
3
+ * Detects the current platform based on the user agent.
4
+ *
5
+ * @returns {Platform} The detected platform: "mac", "windows", or "linux"
6
+ */
7
+ export declare const getPlatform: () => Platform;
8
+ /**
9
+ * Checks if the primary modifier key is pressed based on platform.
10
+ * - Mac: metaKey (Cmd)
11
+ * - Windows/Linux: ctrlKey
12
+ */
13
+ export declare const isModKeyPressed: (event: KeyboardEvent) => boolean;
14
+ /**
15
+ * Checks if the opposite modifier key is pressed (the one NOT used for "mod").
16
+ * This helps detect when a user presses the wrong modifier for their platform.
17
+ * - Mac: ctrlKey (Ctrl pressed when Cmd expected)
18
+ * - Windows/Linux: metaKey (Win key pressed when Ctrl expected)
19
+ */
20
+ export declare const isOppositeModKeyPressed: (event: KeyboardEvent) => boolean;