@primer/react 38.27.0-rc.179829ffc → 38.27.0-rc.1c9d7ead9

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/CHANGELOG.md CHANGED
@@ -10,8 +10,18 @@
10
10
 
11
11
  ### Patch Changes
12
12
 
13
+ - [#7908](https://github.com/primer/react/pull/7908) [`e9a2254`](https://github.com/primer/react/commit/e9a225421df61e7fa62da1b2796972122d266d36) Thanks [@jonrohan](https://github.com/jonrohan)! - `KeybindingHint`: display the `Meta` key correctly on platforms other than macOS and Windows. The `Meta`, `Alt`, and `Mod` keys are now resolved based on the detected platform: Apple platforms (macOS and iOS) show `⌘`/`⌥`, Windows shows `Win`, and all other platforms show `Meta`/`Alt`.
14
+
13
15
  - [#7894](https://github.com/primer/react/pull/7894) [`af4541d`](https://github.com/primer/react/commit/af4541d991c6e583dbf510bd0a0699f244ea00b2) Thanks [@mattcosta7](https://github.com/mattcosta7)! - ActionList: Replace `:has(...)` selectors on `ActionList.Item`, `InactiveButtonWrap`, and `TrailingActionButton` with JS-derived data attributes (`data-has-trailing-action`, `data-trailing-action-loading`, `data-position`, `data-has-label`). Reduces style-recalculation cost on lists that render many items. No visual or behavioral changes.
14
16
 
17
+ - [#7899](https://github.com/primer/react/pull/7899) [`9659ce7`](https://github.com/primer/react/commit/9659ce767760d85c033e43e7e8ecdfdfb452dcb1) Thanks [@mattcosta7](https://github.com/mattcosta7)! - `TreeView`: make rows safer to use with `contain: paint` / `content-visibility: auto` and reduce style-recalc cost on hover/focus in large trees. No visual or layout changes; all changes are either invisible at the default rendering or behind an opt-in CSS containment property the consumer sets.
18
+
19
+ - The current-item indicator (positioned at `left: -8px` of the row container) was being clipped when a consumer applied `contain: paint` to the `<li>` or when the documented `containIntrinsicSize` prop on `TreeView.Item` triggered `content-visibility: auto` on the row container — including for `current` items. Both `.TreeViewItem` and `.TreeViewItemContainer` now declare `overflow-clip-margin: var(--base-size-8)`, which extends the paint-clip edge by 8px on the side the indicator paints. The property is a no-op when no paint containment is active, so default rendering is byte-identical.
20
+ - Skeleton-row hover suppression no longer relies on `:has(.TreeViewItemSkeleton)`, which forced subtree invalidation on every row. `LoadingItem` now communicates with the placeholder `Item` via a module-private context that emits a positive `data-loading` attribute on the `<li>`, and the CSS selector targets that directly. No new public prop.
21
+ - Nesting indicator lines no longer use a root-scope `:hover`/`:focus-within` descendant selector. Color is driven by an inherited `--tree-line-color` custom property set on the root `<ul>`, so a hover or focus change inside the tree updates one property on one element instead of re-matching `.TreeViewItemLevelLine` selectors against every level line in the tree.
22
+ - Fixed a unitless `outline-offset: -2` in the forced-colors focus-ring fallback that browsers were silently dropping (so forced-colors users now actually get a focus indicator on tree items).
23
+ - `.TreeViewItemContainer`'s `grid-template-columns` now declares the `trailingAction` column explicitly (`auto`) so it matches the 5-area `grid-template-areas` declaration (previously the trailing column was implicit `auto`).
24
+
15
25
  ## 38.26.0
16
26
 
17
27
  ### Minor Changes
@@ -1,4 +1,5 @@
1
1
  import type { KeybindingHintProps } from './props';
2
+ import type { Platform } from './platform';
2
3
  /** Indicates the presence of an available keybinding. */
3
4
  export declare const KeybindingHint: import("react").MemoExoticComponent<({ className, ...props }: KeybindingHintProps) => import("react").JSX.Element>;
4
5
  /**
@@ -8,6 +9,10 @@ export declare const KeybindingHint: import("react").MemoExoticComponent<({ clas
8
9
  *
9
10
  * NOTE that this string should _only_ be used when building `aria-label` or `aria-description` props (never rendered
10
11
  * visibly) and should nearly always also be paired with a visible hint for sighted users.
12
+ *
13
+ * The `platform` argument controls how platform-specific keys (such as `Meta`, `Alt`, and `Mod`) are named. For
14
+ * backwards compatibility, a `boolean` may be passed instead, where `true` is treated as `'apple'` and `false` as
15
+ * `'other'`.
11
16
  */
12
- export declare const getAccessibleKeybindingHintString: (sequence: string, isMacOS: boolean) => string;
17
+ export declare const getAccessibleKeybindingHintString: (sequence: string, platform: Platform | boolean) => string;
13
18
  //# sourceMappingURL=KeybindingHint.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"KeybindingHint.d.ts","sourceRoot":"","sources":["../../src/KeybindingHint/KeybindingHint.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,SAAS,CAAA;AAoBhD,yDAAyD;AAEzD,eAAO,MAAM,cAAc,gEAAgC,mBAAmB,iCAI5E,CAAA;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,gDAA2B,CAAA"}
1
+ {"version":3,"file":"KeybindingHint.d.ts","sourceRoot":"","sources":["../../src/KeybindingHint/KeybindingHint.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,SAAS,CAAA;AAEhD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAA;AAmBxC,yDAAyD;AAEzD,eAAO,MAAM,cAAc,gEAAgC,mBAAmB,iCAI5E,CAAA;AAGF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,UAAU,MAAM,EAAE,UAAU,QAAQ,GAAG,OAAO,WACe,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { c } from 'react-compiler-runtime';
2
2
  import { memo } from 'react';
3
- import { accessibleSequenceString, Sequence } from './components/Sequence.js';
3
+ import { Sequence, accessibleSequenceString } from './components/Sequence.js';
4
4
  import classes from './KeybindingHint.module.css.js';
5
5
  import { clsx } from 'clsx';
6
6
  import { jsx } from 'react/jsx-runtime';
@@ -89,7 +89,11 @@ KeybindingHint.displayName = 'KeybindingHint';
89
89
  *
90
90
  * NOTE that this string should _only_ be used when building `aria-label` or `aria-description` props (never rendered
91
91
  * visibly) and should nearly always also be paired with a visible hint for sighted users.
92
+ *
93
+ * The `platform` argument controls how platform-specific keys (such as `Meta`, `Alt`, and `Mod`) are named. For
94
+ * backwards compatibility, a `boolean` may be passed instead, where `true` is treated as `'apple'` and `false` as
95
+ * `'other'`.
92
96
  */
93
- const getAccessibleKeybindingHintString = accessibleSequenceString;
97
+ const getAccessibleKeybindingHintString = (sequence, platform) => accessibleSequenceString(sequence, typeof platform === 'boolean' ? platform ? 'apple' : 'other' : platform);
94
98
 
95
99
  export { KeybindingHint, getAccessibleKeybindingHintString };
@@ -1,5 +1,6 @@
1
1
  import type { KeybindingHintProps } from '../props';
2
+ import type { Platform } from '../platform';
2
3
  export declare const Chord: ({ keys, format, variant, size }: KeybindingHintProps) => import("react").JSX.Element;
3
4
  /** Plain string version of `Chord` for use in `aria` string attributes. */
4
- export declare const accessibleChordString: (chord: string, isMacOS: boolean) => string;
5
+ export declare const accessibleChordString: (chord: string, platform: Platform) => string;
5
6
  //# sourceMappingURL=Chord.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Chord.d.ts","sourceRoot":"","sources":["../../../src/KeybindingHint/components/Chord.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AA+BjD,eAAO,MAAM,KAAK,GAAI,iCAAmE,mBAAmB,gCAsB3G,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,EAAE,SAAS,OAAO,WAGvD,CAAA"}
1
+ {"version":3,"file":"Chord.d.ts","sourceRoot":"","sources":["../../../src/KeybindingHint/components/Chord.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAGjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAA;AA6BzC,eAAO,MAAM,KAAK,GAAI,iCAAmE,mBAAmB,gCAsB3G,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,EAAE,UAAU,QAAQ,WAGzD,CAAA"}
@@ -96,6 +96,6 @@ const Chord = t0 => {
96
96
  };
97
97
 
98
98
  /** Plain string version of `Chord` for use in `aria` string attributes. */
99
- const accessibleChordString = (chord, isMacOS) => splitChord(chord).map(key => accessibleKeyName(key, isMacOS)).join(' ');
99
+ const accessibleChordString = (chord, platform) => splitChord(chord).map(key => accessibleKeyName(key, platform)).join(' ');
100
100
 
101
101
  export { Chord, accessibleChordString };
@@ -1,7 +1,7 @@
1
1
  import { c } from 'react-compiler-runtime';
2
2
  import VisuallyHidden from '../../_VisuallyHidden.js';
3
3
  import { accessibleKeyName, condensedKeyName, fullKeyName } from '../key-names.js';
4
- import { useIsMacOS } from '../../hooks/useIsMacOS.js';
4
+ import { usePlatform } from '../platform.js';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  /** Renders a single key with accessible alternative text. */
@@ -11,12 +11,12 @@ const Key = t0 => {
11
11
  name,
12
12
  format
13
13
  } = t0;
14
- const isMacOS = useIsMacOS();
14
+ const platform = usePlatform();
15
15
  let t1;
16
- if ($[0] !== isMacOS || $[1] !== name) {
17
- t1 = accessibleKeyName(name, isMacOS);
18
- $[0] = isMacOS;
19
- $[1] = name;
16
+ if ($[0] !== name || $[1] !== platform) {
17
+ t1 = accessibleKeyName(name, platform);
18
+ $[0] = name;
19
+ $[1] = platform;
20
20
  $[2] = t1;
21
21
  } else {
22
22
  t1 = $[2];
@@ -32,11 +32,11 @@ const Key = t0 => {
32
32
  t2 = $[4];
33
33
  }
34
34
  let t3;
35
- if ($[5] !== format || $[6] !== isMacOS || $[7] !== name) {
36
- t3 = format === "condensed" ? condensedKeyName(name, isMacOS) : fullKeyName(name, isMacOS);
35
+ if ($[5] !== format || $[6] !== name || $[7] !== platform) {
36
+ t3 = format === "condensed" ? condensedKeyName(name, platform) : fullKeyName(name, platform);
37
37
  $[5] = format;
38
- $[6] = isMacOS;
39
- $[7] = name;
38
+ $[6] = name;
39
+ $[7] = platform;
40
40
  $[8] = t3;
41
41
  } else {
42
42
  t3 = $[8];
@@ -1,5 +1,6 @@
1
1
  import type { KeybindingHintProps } from '../props';
2
+ import type { Platform } from '../platform';
2
3
  export declare const Sequence: ({ keys, ...chordProps }: KeybindingHintProps) => import("react").JSX.Element[];
3
4
  /** Plain string version of `Sequence` for use in `aria` string attributes. */
4
- export declare const accessibleSequenceString: (sequence: string, isMacOS: boolean) => string;
5
+ export declare const accessibleSequenceString: (sequence: string, platform: Platform) => string;
5
6
  //# sourceMappingURL=Sequence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sequence.d.ts","sourceRoot":"","sources":["../../../src/KeybindingHint/components/Sequence.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAMjD,eAAO,MAAM,QAAQ,GAAI,yBAAuB,mBAAmB,kCAa/D,CAAA;AAEJ,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,GAAI,UAAU,MAAM,EAAE,SAAS,OAAO,WAGxD,CAAA"}
1
+ {"version":3,"file":"Sequence.d.ts","sourceRoot":"","sources":["../../../src/KeybindingHint/components/Sequence.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAA;AAGjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAA;AAIzC,eAAO,MAAM,QAAQ,GAAI,yBAAuB,mBAAmB,kCAa/D,CAAA;AAEJ,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,GAAI,UAAU,MAAM,EAAE,UAAU,QAAQ,WAG1D,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { Fragment } from 'react';
2
2
  import VisuallyHidden from '../../_VisuallyHidden.js';
3
- import { accessibleChordString, Chord } from './Chord.js';
3
+ import { Chord, accessibleChordString } from './Chord.js';
4
4
  import { jsxs, Fragment as Fragment$1, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  const splitSequence = sequence => sequence.split(' ');
@@ -21,6 +21,6 @@ const Sequence = ({
21
21
  }, i));
22
22
 
23
23
  /** Plain string version of `Sequence` for use in `aria` string attributes. */
24
- const accessibleSequenceString = (sequence, isMacOS) => splitSequence(sequence).map(chord => accessibleChordString(chord, isMacOS)).join(' then ');
24
+ const accessibleSequenceString = (sequence, platform) => splitSequence(sequence).map(chord => accessibleChordString(chord, platform)).join(' then ');
25
25
 
26
26
  export { Sequence, accessibleSequenceString };
@@ -1,16 +1,17 @@
1
+ import type { Platform } from './platform';
1
2
  /**
2
3
  * Short-form iconic versions of keys. These should be intuitive (not archaic) and match icons on keyboards.
3
4
  */
4
- export declare const condensedKeyName: (key: string, isMacOS: boolean) => string;
5
+ export declare const condensedKeyName: (key: string, platform: Platform) => string;
5
6
  /**
6
7
  * Specific key displays for 'full' format. We still do show some icons (ie punctuation)
7
8
  * because that's more intuitive, but for the rest of keys we show the standard key name.
8
9
  */
9
- export declare const fullKeyName: (key: string, isMacOS: boolean) => string;
10
+ export declare const fullKeyName: (key: string, platform: Platform) => string;
10
11
  /**
11
12
  * Accessible key names intended to be read by a screen reader. This prevents screen
12
13
  * readers from expressing punctuation in speech, ie, reading a long pause instead of the
13
14
  * word "period".
14
15
  */
15
- export declare const accessibleKeyName: (key: string, isMacOS: boolean) => string;
16
+ export declare const accessibleKeyName: (key: string, platform: Platform) => string;
16
17
  //# sourceMappingURL=key-names.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"key-names.d.ts","sourceRoot":"","sources":["../../src/KeybindingHint/key-names.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,EAAE,SAAS,OAAO,WAwBlC,CAAA;AAE5B;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,SAAS,OAAO,WAa7B,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,EAAE,SAAS,OAAO,WAmDjC,CAAA"}
1
+ {"version":3,"file":"key-names.d.ts","sourceRoot":"","sources":["../../src/KeybindingHint/key-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAA;AAUxC;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,EAAE,UAAU,QAAQ,WAwBpC,CAAA;AAE5B;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,UAAU,QAAQ,WAc/B,CAAA;AAE5B;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,EAAE,UAAU,QAAQ,WAmDnC,CAAA"}
@@ -12,15 +12,15 @@ const capitalize = ([first, ...rest]) => {
12
12
  /**
13
13
  * Short-form iconic versions of keys. These should be intuitive (not archaic) and match icons on keyboards.
14
14
  */
15
- const condensedKeyName = (key, isMacOS) => {
15
+ const condensedKeyName = (key, platform) => {
16
16
  var _alt$control$shift$me;
17
17
  return (_alt$control$shift$me = {
18
- alt: isMacOS ? '⌥' : 'Alt',
18
+ alt: platform === 'apple' ? '⌥' : 'Alt',
19
19
  // the alt key _is_ the option key on MacOS - in the browser there is no "option" key
20
20
  control: '⌃',
21
21
  shift: '⇧',
22
- meta: isMacOS ? '⌘' : 'Win',
23
- mod: isMacOS ? '⌘' : '⌃',
22
+ meta: platform === 'apple' ? '⌘' : platform === 'windows' ? 'Win' : 'Meta',
23
+ mod: platform === 'apple' ? '⌘' : '⌃',
24
24
  pageup: 'PgUp',
25
25
  pagedown: 'PgDn',
26
26
  arrowup: '↑',
@@ -47,11 +47,12 @@ const condensedKeyName = (key, isMacOS) => {
47
47
  * Specific key displays for 'full' format. We still do show some icons (ie punctuation)
48
48
  * because that's more intuitive, but for the rest of keys we show the standard key name.
49
49
  */
50
- const fullKeyName = (key, isMacOS) => {
51
- var _alt$mod$$pageup$pag;
52
- return (_alt$mod$$pageup$pag = {
53
- alt: isMacOS ? 'Option' : 'Alt',
54
- mod: isMacOS ? 'Command' : 'Control',
50
+ const fullKeyName = (key, platform) => {
51
+ var _alt$meta$mod$$pageu;
52
+ return (_alt$meta$mod$$pageu = {
53
+ alt: platform === 'apple' ? 'Option' : 'Alt',
54
+ meta: platform === 'apple' ? 'Command' : platform === 'windows' ? 'Windows' : 'Meta',
55
+ mod: platform === 'apple' ? 'Command' : 'Control',
55
56
  '+': 'Plus',
56
57
  pageup: 'Page Up',
57
58
  pagedown: 'Page Down',
@@ -61,7 +62,7 @@ const fullKeyName = (key, isMacOS) => {
61
62
  arrowright: 'Right Arrow',
62
63
  capslock: 'Caps Lock',
63
64
  printscreen: 'Print Screen'
64
- }[key]) !== null && _alt$mod$$pageup$pag !== void 0 ? _alt$mod$$pageup$pag : capitalize(key);
65
+ }[key]) !== null && _alt$meta$mod$$pageu !== void 0 ? _alt$meta$mod$$pageu : capitalize(key);
65
66
  };
66
67
 
67
68
  /**
@@ -69,12 +70,12 @@ const fullKeyName = (key, isMacOS) => {
69
70
  * readers from expressing punctuation in speech, ie, reading a long pause instead of the
70
71
  * word "period".
71
72
  */
72
- const accessibleKeyName = (key, isMacOS) => {
73
+ const accessibleKeyName = (key, platform) => {
73
74
  var _alt$meta$mod$pageup$;
74
75
  return (_alt$meta$mod$pageup$ = {
75
- alt: isMacOS ? 'option' : 'alt',
76
- meta: isMacOS ? 'command' : 'Windows',
77
- mod: isMacOS ? 'command' : 'control',
76
+ alt: platform === 'apple' ? 'option' : 'alt',
77
+ meta: platform === 'apple' ? 'command' : platform === 'windows' ? 'Windows' : 'meta',
78
+ mod: platform === 'apple' ? 'command' : 'control',
78
79
  // Screen readers may not be able to pronounce concatenated words - this provides a better experience
79
80
  pageup: 'page up',
80
81
  pagedown: 'page down',
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The platform categories that affect how keyboard shortcut keys are displayed.
3
+ *
4
+ * - `apple`: Apple platforms (macOS and iOS/iPadOS), which use the Command and Option keys.
5
+ * - `windows`: Windows, which uses the Windows (Meta) key.
6
+ * - `other`: Any other platform (e.g. Linux, Android), where the Meta key does not map to a
7
+ * consistent label.
8
+ */
9
+ export type Platform = 'apple' | 'windows' | 'other';
10
+ export declare const PlatformOverrideProvider: import("react").Provider<Platform | null>;
11
+ /**
12
+ * SSR-safe hook for determining the current platform category used when displaying
13
+ * keyboard shortcut keys.
14
+ *
15
+ * Mirrors the approach of `useIsMacOS`: on the client it reads the real value immediately,
16
+ * and on the server it returns a safe default (`'other'`).
17
+ *
18
+ * If a `PlatformOverrideProvider` is present with a non-null value, that value is used
19
+ * instead of the detected platform.
20
+ */
21
+ export declare function usePlatform(): Platform;
22
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/KeybindingHint/platform.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAA;AAkCpD,eAAO,MAAM,wBAAwB,2CAAmC,CAAA;AAExE;;;;;;;;;GASG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAItC"}
@@ -0,0 +1,61 @@
1
+ import { isMacOS } from '@primer/behaviors/utils';
2
+ import { useContext, useSyncExternalStore, createContext } from 'react';
3
+
4
+ /**
5
+ * The platform categories that affect how keyboard shortcut keys are displayed.
6
+ *
7
+ * - `apple`: Apple platforms (macOS and iOS/iPadOS), which use the Command and Option keys.
8
+ * - `windows`: Windows, which uses the Windows (Meta) key.
9
+ * - `other`: Any other platform (e.g. Linux, Android), where the Meta key does not map to a
10
+ * consistent label.
11
+ */
12
+
13
+ // No-op. The platform never changes at runtime, so there is nothing to
14
+ // subscribe to. Hoisted to avoid creating a new function on every call.
15
+ const subscribe = () => () => {};
16
+
17
+ /** SSR-unsafe detection of iOS/iPadOS (in addition to macOS, which is detected separately). */
18
+ const ssrUnsafeIsIOS = () => {
19
+ if (typeof navigator === 'undefined') return false;
20
+ return /iphone|ipad|ipod/i.test(navigator.platform) || /iphone|ipad|ipod/i.test(navigator.userAgent);
21
+ };
22
+
23
+ /** SSR-unsafe detection of Windows. */
24
+ const ssrUnsafeIsWindows = () => {
25
+ if (typeof navigator === 'undefined') return false;
26
+ return /^win/i.test(navigator.platform);
27
+ };
28
+ const getSnapshot = () => {
29
+ if (isMacOS() || ssrUnsafeIsIOS()) return 'apple';
30
+ if (ssrUnsafeIsWindows()) return 'windows';
31
+ return 'other';
32
+ };
33
+
34
+ // Safe default for SSR since we can't detect the platform on the server.
35
+ const getServerSnapshot = () => 'other';
36
+
37
+ /**
38
+ * Allows overriding the detected platform. This is primarily intended for testing and
39
+ * Storybook, where we want to preview how keyboard hints render on platforms other than the
40
+ * one actually running. A `null` value (the default) means "use the detected platform".
41
+ */
42
+ const PlatformOverrideContext = /*#__PURE__*/createContext(null);
43
+ PlatformOverrideContext.Provider;
44
+
45
+ /**
46
+ * SSR-safe hook for determining the current platform category used when displaying
47
+ * keyboard shortcut keys.
48
+ *
49
+ * Mirrors the approach of `useIsMacOS`: on the client it reads the real value immediately,
50
+ * and on the server it returns a safe default (`'other'`).
51
+ *
52
+ * If a `PlatformOverrideProvider` is present with a non-null value, that value is used
53
+ * instead of the detected platform.
54
+ */
55
+ function usePlatform() {
56
+ const override = useContext(PlatformOverrideContext);
57
+ const detected = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
58
+ return override !== null && override !== void 0 ? override : detected;
59
+ }
60
+
61
+ export { usePlatform };
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/TooltipV2/Tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiD,KAAK,yBAAyB,EAAC,MAAM,OAAO,CAAA;AAS3G,OAAO,EAAoD,KAAK,mBAAmB,EAAC,MAAM,mBAAmB,CAAA;AAG7G,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAA;AAE9C,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAA;AAChF,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,iBAAiB,CAAC;IACjD,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,GAAG,aAAa,CAAA;IAC9B,cAAc,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAA;IACjF;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACnC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC,CAAC,GACA,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;AAiEnC,eAAO,MAAM,cAAc;gBAAoC,MAAM;EAAM,CAAA;AAI3E,eAAO,MAAM,OAAO,EAAE,yBAAyB,CAC7C,KAAK,CAAC,eAAe,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CACvE,GACC,UAkSD,CAAA"}
1
+ {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/TooltipV2/Tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAiD,KAAK,yBAAyB,EAAC,MAAM,OAAO,CAAA;AAS3G,OAAO,EAAoD,KAAK,mBAAmB,EAAC,MAAM,mBAAmB,CAAA;AAI7G,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAA;AAE9C,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAA;AAChF,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,iBAAiB,CAAC;IACjD,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,GAAG,aAAa,CAAA;IAC9B,cAAc,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAA;IACjF;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACnC;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC,CAAC,GACA,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;AAiEnC,eAAO,MAAM,cAAc;gBAAoC,MAAM;EAAM,CAAA;AAI3E,eAAO,MAAM,OAAO,EAAE,yBAAyB,CAC7C,KAAK,CAAC,eAAe,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CACvE,GACC,UAkSD,CAAA"}
@@ -5,13 +5,13 @@ import { getAnchoredPosition } from '@primer/behaviors';
5
5
  import { isSupported, apply } from '@oddbird/popover-polyfill/fn';
6
6
  import { clsx } from 'clsx';
7
7
  import classes from './Tooltip.module.css.js';
8
+ import { usePlatform } from '../KeybindingHint/platform.js';
8
9
  import VisuallyHidden from '../_VisuallyHidden.js';
9
10
  import useSafeTimeout from '../hooks/useSafeTimeout.js';
10
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
12
  import { useId } from '../hooks/useId.js';
12
13
  import { useProvidedRefOrCreate } from '../hooks/useProvidedRefOrCreate.js';
13
14
  import { useOnEscapePress } from '../hooks/useOnEscapePress.js';
14
- import { useIsMacOS } from '../hooks/useIsMacOS.js';
15
15
  import { getAccessibleKeybindingHintString, KeybindingHint } from '../KeybindingHint/KeybindingHint.js';
16
16
 
17
17
  // map tooltip direction to anchoredPosition props
@@ -195,7 +195,7 @@ const Tooltip = /*#__PURE__*/React.forwardRef(({
195
195
  closeTooltip();
196
196
  }
197
197
  }, [isPopoverOpen]);
198
- const isMacOS = useIsMacOS();
198
+ const platform = usePlatform();
199
199
  const hasAriaLabel = 'aria-label' in rest;
200
200
 
201
201
  // Normalize keybindingHint to an array for uniform rendering
@@ -290,7 +290,7 @@ const Tooltip = /*#__PURE__*/React.forwardRef(({
290
290
  children: [/*#__PURE__*/jsxs("span", {
291
291
  id: hasAriaLabel ? undefined : tooltipId,
292
292
  children: [text, /*#__PURE__*/jsxs(VisuallyHidden, {
293
- children: ["(", keybindingHints.map(hint => getAccessibleKeybindingHintString(hint, isMacOS)).join(' or '), ")"]
293
+ children: ["(", keybindingHints.map(hint => getAccessibleKeybindingHintString(hint, platform)).join(' or '), ")"]
294
294
  })]
295
295
  }), /*#__PURE__*/jsx("span", {
296
296
  className: clsx(classes.KeybindingHintContainer, text && classes.HasTextBefore, keybindingHints.length > 1 && classes.HasMultipleHints),
@@ -0,0 +1,2 @@
1
+ .prc-TreeView-TreeViewRootUlStyles-Mzrmj{list-style:none;margin:0;padding:0}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f{outline:none;overflow-clip-margin:var(--base-size-8,.5rem)}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f):focus-visible>div,:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f).focus-visible>div{box-shadow:var(--boxShadow-thick,inset 0 0 0 .125rem) var(--fgColor-accent,#0969da)}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f):focus-visible>div,:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f).focus-visible>div{outline:2px solid HighlightText;outline-offset:-2px}}[data-has-leading-action]:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f){--has-leading-action:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ{--level:1;--toggle-width:1rem;--min-item-height:2rem;border-radius:var(--borderRadius-medium,.375rem);color:var(--fgColor-default,#1f2328);cursor:pointer;display:grid;font-size:var(--text-body-size-medium,.875rem);grid-template-areas:"spacer leadingAction toggle content trailingAction";grid-template-columns:var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr auto;overflow-clip-margin:var(--base-size-8,.5rem);position:relative;width:100%;--leading-action-width:calc(var(--has-leading-action, 0)*1.5rem);--spacer-width:calc((var(--level) - 1)*(var(--toggle-width)/2))}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):hover{background-color:var(--control-transparent-bgColor-hover,#818b981a)}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):hover{outline:2px solid transparent;outline-offset:-2px}}@media (pointer:coarse){.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ{--toggle-width:1.5rem;--min-item-height:2.75rem}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-omit-spacer=true]) .prc-TreeView-TreeViewItemContainer-z6qqQ{grid-template-columns:0 0 0 1fr auto}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f:where([data-loading])>.prc-TreeView-TreeViewItemContainer-z6qqQ:hover{background-color:transparent;cursor:default}@media (forced-colors:active){.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f:where([data-loading])>.prc-TreeView-TreeViewItemContainer-z6qqQ:hover{outline:none}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ{background-color:var(--control-transparent-bgColor-selected,#818b9826)}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ):after{background-color:var(--fgColor-accent,#0969da);border-radius:var(--borderRadius-medium,.375rem);content:"";height:1.5rem;left:calc(var(--base-size-8,.5rem)*-1);position:absolute;top:calc(50% - var(--base-size-12,.75rem));width:.25rem}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ):after{background-color:HighlightText}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggle-hq3Xq{align-items:flex-start;color:var(--fgColor-muted,#59636e);display:flex;grid-area:toggle;height:100%;justify-content:center;padding-top:calc(var(--min-item-height)/2 - var(--base-size-12,.75rem)/2)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggleHover-H9tbt:hover{background-color:var(--control-transparent-bgColor-hover,#818b981a)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggleEnd-nWt9I{border-bottom-left-radius:var(--borderRadius-medium,.375rem);border-top-left-radius:var(--borderRadius-medium,.375rem)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContent-RKsCI{display:flex;gap:var(--stack-gap-condensed,.5rem);grid-area:content;height:100%;line-height:var(--custom-line-height,var(--text-body-lineHeight-medium,1.4285));padding:0 var(--base-size-8,.5rem);padding-bottom:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2);padding-top:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContentText-FFaKp{flex:1 1 auto;width:0}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-truncate-text=true]) .prc-TreeView-TreeViewItemContentText-FFaKp{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-truncate-text=false]) .prc-TreeView-TreeViewItemContentText-FFaKp{word-break:break-word}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemVisual-naWzj{align-items:center;color:var(--fgColor-muted,#59636e);display:flex;height:var(--custom-line-height,1.3rem)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLeadingAction-vmOIV{color:var(--fgColor-muted,#59636e);display:flex;grid-area:leadingAction}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLeadingAction-vmOIV)>button{flex-shrink:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemTrailingAction-h9M5g{color:var(--fgColor-muted,#59636e);display:flex;grid-area:trailingAction}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemTrailingActionButton-pCTdP{flex-shrink:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLevelLine-F-0-2{border-right:var(--borderWidth-thin,.0625rem) solid;border-color:var(--tree-line-color,var(--borderColor-muted,#d1d9e0b3));height:100%;width:100%}@media (hover:hover){.prc-TreeView-TreeViewRootUlStyles-Mzrmj{--tree-line-color:transparent}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:focus-within,.prc-TreeView-TreeViewRootUlStyles-Mzrmj:hover{--tree-line-color:var(--borderColor-muted,#d1d9e0b3)}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewDirectoryIcon-yP1oY{color:var(--treeViewItem-leadingVisual-iconColor-rest,#54aeff);display:grid}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewVisuallyHidden-1N8xK{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd{align-items:center;column-gap:.5rem;display:flex;height:2rem}@media (pointer:coarse){.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd{height:2.75rem}}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+1){--tree-item-loading-width:67%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+2){--tree-item-loading-width:47%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+3){--tree-item-loading-width:73%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+4){--tree-item-loading-width:64%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+5){--tree-item-loading-width:50%}.prc-TreeView-TreeItemSkeletonTextStyles-8VJ-g{width:var(--tree-item-loading-width,67%)}
2
+ /*# sourceMappingURL=TreeView-1bf45a33.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/TreeView/TreeView.module.css.js"],"names":[],"mappings":"AAAA,yCAGE,eAAgB,CADhB,QAAS,CADT,SA8PF,CA7OE,0EAQE,YAAa,CADb,6CAgBF,CAbE,kMAEE,mFAMF,CAJE,8BAJF,kMAKI,+BAAgC,CAChC,mBAEJ,CADE,CAGF,wGACE,sBACF,CAGF,mFACE,SAAU,CACV,mBAAoB,CACpB,sBAAuB,CAcvB,gDAAyC,CAFzC,oCAA6B,CAC7B,cAAe,CAVf,YAAa,CAQb,8CAAuC,CAKvC,wEAAyE,CADzE,kGAAmG,CALnG,6CAAwC,CARxC,iBAAkB,CAElB,UAAW,CAcX,gEAAmE,CACnE,+DAeF,CAbE,8FACE,mEAMF,CAJE,8BAHF,8FAII,6BAA8B,CAC9B,mBAEJ,CADE,CAGF,wBAjCF,mFAkCI,qBAAsB,CACtB,yBAEJ,CADE,CAGF,kHACE,oCACF,CAOA,gJAEE,4BAA6B,CAD7B,cAMF,CAHE,8BAJF,gJAKI,YAEJ,CADE,CAGF,uIACE,sEAwBF,CApBE,kJAaE,8CAAuC,CACvC,gDAAyC,CARzC,UAAW,CADX,aAAc,CAFd,sCAAmC,CAFnC,iBAAkB,CAClB,0CAAoC,CAEpC,YAeF,CAHE,8BAhBF,kJAiBI,8BAEJ,CADE,CAIJ,gFAWE,sBAAuB,CAHvB,kCAA2B,CAP3B,YAAa,CAQb,gBAAiB,CAPjB,WAAY,CAQZ,sBAAuB,CAHvB,yEAKF,CAEA,2FACE,mEACF,CAEA,mFAEE,4DAAqD,CADrD,yDAEF,CAEA,iFACE,YAAa,CAWb,oCAA+B,CAD/B,iBAAkB,CATlB,WAAY,CAQZ,+EAAkF,CAPlF,kCAA6B,CAM7B,mFAAsF,CAFtF,gFAMF,CAEA,qFACE,aAAc,CACd,OACF,CAEA,sHACE,eAAgB,CAChB,sBAAuB,CACvB,kBACF,CAEA,uHAEE,qBACF,CAEA,gFAOE,kBAAmB,CADnB,kCAA2B,CAL3B,YAAa,CAIb,uCAGF,CAEA,uFAEE,kCAA2B,CAD3B,YAAa,CAEb,uBAKF,CAHE,mGACE,aACF,CAGF,wFAEE,kCAA2B,CAD3B,YAAa,CAEb,wBACF,CAEA,8FACE,aACF,CAEA,mFAGE,mDAA2C,CAW3C,sEAA8D,CAZ9D,WAAY,CADZ,UAcF,CAEA,qBApOF,yCAqOI,6BA0BJ,CAxBI,qGAEE,oDACF,CACF,CAEA,mFAEE,8DAAuD,CADvD,YAEF,CAEA,oFAGE,UAAW,CAGX,WAAY,CACZ,eAAgB,CAHhB,SAAU,CAHV,iBAAkB,CAClB,SAAU,CAOV,kBAAsB,CAEtB,cAAe,CADf,kBAEF,CAGF,uDAEE,kBAAmB,CACnB,gBAAkB,CAFlB,YAAa,CAGb,WAyBF,CAvBE,wBANF,uDAOI,cAsBJ,CArBE,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAGF,+CACE,wCACF","file":"TreeView-1bf45a33.css","sourcesContent":[".TreeViewRootUlStyles {\n padding: 0;\n margin: 0;\n list-style: none;\n\n /*\n * WARNING: This is a performance optimization.\n *\n * We define styles for the tree items at the root level of the tree\n * to avoid recomputing the styles for each item when the tree updates.\n * We're sacrificing maintainability for performance because TreeView\n * needs to be performant enough to handle large trees (thousands of items).\n *\n * This is intended to be a temporary solution until we can improve the\n * performance of our styling patterns.\n *\n * Do NOT copy this pattern without understanding the tradeoffs.\n */\n .TreeViewItem {\n /*\n * `overflow-clip-margin` extends the paint clip edge by 8px so the current-item indicator\n * (positioned at `left: -8px` of the row container) remains visible when a consumer applies\n * `contain: paint` (or `contain: strict`, or `content-visibility: auto`) to this `<li>`. Has\n * no effect when no paint containment is active, so default rendering is unchanged.\n */\n overflow-clip-margin: var(--base-size-8);\n outline: none;\n\n &:focus-visible > div,\n &:global(.focus-visible) > div {\n box-shadow: var(--boxShadow-thick) var(--fgColor-accent);\n\n @media (forced-colors: active) {\n outline: 2px solid HighlightText;\n outline-offset: -2px;\n }\n }\n\n &[data-has-leading-action] {\n --has-leading-action: 1;\n }\n }\n\n .TreeViewItemContainer {\n --level: 1;\n --toggle-width: 1rem;\n --min-item-height: 2rem;\n\n position: relative;\n display: grid;\n width: 100%;\n /*\n * Mirrors the `overflow-clip-margin` on `.TreeViewItem` so the indicator also stays\n * visible when `containIntrinsicSize` is set on this row (which sets\n * `content-visibility: auto` on this container and implies paint containment).\n */\n overflow-clip-margin: var(--base-size-8);\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-default);\n cursor: pointer;\n border-radius: var(--borderRadius-medium);\n grid-template-columns: var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr auto;\n grid-template-areas: 'spacer leadingAction toggle content trailingAction';\n\n --leading-action-width: calc(var(--has-leading-action, 0) * 1.5rem);\n --spacer-width: calc(calc(var(--level) - 1) * (var(--toggle-width) / 2));\n\n &:hover {\n background-color: var(--control-transparent-bgColor-hover);\n\n @media (forced-colors: active) {\n outline: 2px solid transparent;\n outline-offset: -2px;\n }\n }\n\n @media (pointer: coarse) {\n --toggle-width: 1.5rem;\n --min-item-height: 2.75rem;\n }\n }\n\n &:where([data-omit-spacer='true']) .TreeViewItemContainer {\n grid-template-columns: 0 0 0 1fr auto;\n }\n\n /*\n * Suppress hover affordances on rows being used as skeleton loading placeholders.\n * Marked positively via `data-loading` from `LoadingItem` so we avoid the broad\n * invalidation cost of `:has(.TreeViewItemSkeleton)` across every row in large trees.\n */\n .TreeViewItem:where([data-loading]) > .TreeViewItemContainer:hover {\n cursor: default;\n background-color: transparent;\n\n @media (forced-colors: active) {\n outline: none;\n }\n }\n\n .TreeViewItem[aria-current='true'] > .TreeViewItemContainer {\n background-color: var(--control-transparent-bgColor-selected);\n\n /* Current item indicator */\n /* stylelint-disable-next-line selector-max-specificity */\n &::after {\n position: absolute;\n top: calc(50% - var(--base-size-12));\n left: calc(-1 * var(--base-size-8));\n width: 0.25rem;\n height: 1.5rem;\n content: '';\n\n /*\n * Use fgColor accent for consistency across all themes. Using the \"correct\" variable,\n * --bgColor-accent-emphasis, causes vrt failures for dark high contrast mode\n */\n /* stylelint-disable-next-line primer/colors */\n background-color: var(--fgColor-accent);\n border-radius: var(--borderRadius-medium);\n\n @media (forced-colors: active) {\n background-color: HighlightText;\n }\n }\n }\n\n .TreeViewItemToggle {\n display: flex;\n height: 100%;\n\n /* The toggle should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc(var(--min-item-height) / 2 - var(--base-size-12) / 2);\n color: var(--fgColor-muted);\n grid-area: toggle;\n justify-content: center;\n align-items: flex-start;\n }\n\n .TreeViewItemToggleHover:hover {\n background-color: var(--control-transparent-bgColor-hover);\n }\n\n .TreeViewItemToggleEnd {\n border-top-left-radius: var(--borderRadius-medium);\n border-bottom-left-radius: var(--borderRadius-medium);\n }\n\n .TreeViewItemContent {\n display: flex;\n height: 100%;\n padding: 0 var(--base-size-8);\n\n /* The dynamic top and bottom padding to maintain the minimum item height for single line items */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n /* stylelint-disable-next-line primer/spacing */\n padding-bottom: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n line-height: var(--custom-line-height, var(--text-body-lineHeight-medium, 1.4285));\n grid-area: content;\n gap: var(--stack-gap-condensed);\n }\n\n .TreeViewItemContentText {\n flex: 1 1 auto;\n width: 0;\n }\n\n &:where([data-truncate-text='true']) .TreeViewItemContentText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &:where([data-truncate-text='false']) .TreeViewItemContentText {\n /* stylelint-disable-next-line declaration-property-value-keyword-no-deprecated */\n word-break: break-word;\n }\n\n .TreeViewItemVisual {\n display: flex;\n\n /* The visual icons should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n height: var(--custom-line-height, 1.3rem);\n color: var(--fgColor-muted);\n align-items: center;\n }\n\n .TreeViewItemLeadingAction {\n display: flex;\n color: var(--fgColor-muted);\n grid-area: leadingAction;\n\n & > button {\n flex-shrink: 1;\n }\n }\n\n .TreeViewItemTrailingAction {\n display: flex;\n color: var(--fgColor-muted);\n grid-area: trailingAction;\n }\n\n .TreeViewItemTrailingActionButton {\n flex-shrink: 1;\n }\n\n .TreeViewItemLevelLine {\n width: 100%;\n height: 100%;\n border-right: var(--borderWidth-thin) solid;\n\n /*\n * `--tree-line-color` is set on the root `<ul>` and inherited down. On coarse pointers it\n * stays unset and falls back to `muted` (lines always visible). On hover-capable devices it\n * is initialized to `transparent` on the root and flipped to `muted` while the tree is\n * hovered or focused, so the browser only has to propagate a single inherited custom\n * property instead of re-matching `.TreeViewItemLevelLine` descendant selectors on every\n * hover/focus change inside large trees.\n */\n /* stylelint-disable-next-line primer/colors -- private custom property, defaults to a Primer token */\n border-color: var(--tree-line-color, var(--borderColor-muted));\n }\n\n @media (hover: hover) {\n --tree-line-color: transparent;\n\n &:hover,\n &:focus-within {\n --tree-line-color: var(--borderColor-muted);\n }\n }\n\n .TreeViewDirectoryIcon {\n display: grid;\n color: var(--treeViewItem-leadingVisual-iconColor-rest);\n }\n\n .TreeViewVisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n /* stylelint-disable-next-line primer/spacing */\n margin: -1px;\n overflow: hidden;\n /* stylelint-disable-next-line property-no-deprecated */\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n }\n}\n\n.TreeViewSkeletonItemContainerStyle {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n height: 2rem;\n\n @media (pointer: coarse) {\n height: 2.75rem;\n }\n\n &:nth-of-type(5n + 1) {\n --tree-item-loading-width: 67%;\n }\n\n &:nth-of-type(5n + 2) {\n --tree-item-loading-width: 47%;\n }\n\n &:nth-of-type(5n + 3) {\n --tree-item-loading-width: 73%;\n }\n\n &:nth-of-type(5n + 4) {\n --tree-item-loading-width: 64%;\n }\n\n &:nth-of-type(5n + 5) {\n --tree-item-loading-width: 50%;\n }\n}\n\n.TreeItemSkeletonTextStyles {\n width: var(--tree-item-loading-width, 67%);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"TreeView.d.ts","sourceRoot":"","sources":["../../src/TreeView/TreeView.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TreeView.d.ts","sourceRoot":"","sources":["../../src/TreeView/TreeView.tsx"],"names":[],"mappings":"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"}
@@ -15,9 +15,9 @@ import { useTypeahead } from './useTypeahead.js';
15
15
  import { IconButton } from '../Button/IconButton.js';
16
16
  import { ButtonComponent } from '../Button/Button.js';
17
17
  import { ActionList } from '../ActionList/index.js';
18
+ import { usePlatform } from '../KeybindingHint/platform.js';
18
19
  import { isSlot } from '../utils/is-slot.js';
19
20
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
20
- import { useIsMacOS } from '../hooks/useIsMacOS.js';
21
21
  import { AriaStatus } from '../live-region/AriaStatus.js';
22
22
  import Spinner from '../Spinner/Spinner.js';
23
23
  import { Tooltip } from '../TooltipV2/Tooltip.js';
@@ -46,6 +46,11 @@ const ItemContext = /*#__PURE__*/React.createContext({
46
46
  trailingActionId: ''
47
47
  });
48
48
 
49
+ // Module-private channel from LoadingItem to the Item it renders. Lets us mark the placeholder
50
+ // row with `data-loading` (used by CSS to suppress hover affordances) without exposing an
51
+ // internal-only prop on the public TreeViewItemProps API.
52
+ const LoadingPlaceholderContext = /*#__PURE__*/React.createContext(false);
53
+
49
54
  // ----------------------------------------------------------------------------
50
55
  // TreeView
51
56
 
@@ -303,6 +308,7 @@ const Item = /*#__PURE__*/React.forwardRef(({
303
308
  const {
304
309
  level
305
310
  } = React.useContext(ItemContext);
311
+ const isLoadingPlaceholder = React.useContext(LoadingPlaceholderContext);
306
312
  const {
307
313
  hasSubTree,
308
314
  subTree,
@@ -311,7 +317,7 @@ const Item = /*#__PURE__*/React.forwardRef(({
311
317
  const [isSubTreeEmpty, setIsSubTreeEmpty] = React.useState(!hasSubTree);
312
318
  const [actionCommandPressed, setActionCommandPressed] = React.useState(false);
313
319
  const [isFocused, setIsFocused] = React.useState(false);
314
- const isMacOS = useIsMacOS();
320
+ const platform = usePlatform();
315
321
 
316
322
  // Set the expanded state and cache it
317
323
  const setIsExpandedWithCache = React.useCallback(
@@ -372,8 +378,8 @@ const Item = /*#__PURE__*/React.forwardRef(({
372
378
  }
373
379
  }, [onSelect, setIsExpandedWithCache, toggle, activateActionsDialog]);
374
380
  const ariaDescribedByIds = [slots.leadingVisual ? leadingVisualId : null, slots.trailingVisual ? trailingVisualId : null].filter(Boolean);
375
- const shortcut = `Shift+${isMacOS ? 'Meta' : 'Control'}+U`;
376
- const trailingActionShortcutText = `Press (${getAccessibleKeybindingHintString(shortcut, isMacOS)}) for more actions.`;
381
+ const shortcut = `Shift+${platform === 'apple' ? 'Meta' : 'Control'}+U`;
382
+ const trailingActionShortcutText = `Press (${getAccessibleKeybindingHintString(shortcut, platform)}) for more actions.`;
377
383
  return /*#__PURE__*/jsx(ItemContext.Provider, {
378
384
  value: {
379
385
  itemId,
@@ -400,6 +406,7 @@ const Item = /*#__PURE__*/React.forwardRef(({
400
406
  "aria-current": isCurrentItem ? 'true' : undefined,
401
407
  "aria-selected": isFocused ? 'true' : 'false',
402
408
  "data-has-leading-action": slots.leadingAction ? true : undefined,
409
+ "data-loading": isLoadingPlaceholder ? true : undefined,
403
410
  onKeyDown: handleKeyDown,
404
411
  onFocus: event_1 => {
405
412
  // Defer scroll to the next animation frame so that rapid keyboard
@@ -778,10 +785,13 @@ const LoadingItem = /*#__PURE__*/React.forwardRef((t0, ref) => {
778
785
  }
779
786
  let t4;
780
787
  if ($[5] !== itemId || $[6] !== ref || $[7] !== t1 || $[8] !== t3) {
781
- t4 = /*#__PURE__*/jsxs(Item, {
782
- id: itemId,
783
- ref: ref,
784
- children: [t1, t3]
788
+ t4 = /*#__PURE__*/jsx(LoadingPlaceholderContext.Provider, {
789
+ value: true,
790
+ children: /*#__PURE__*/jsxs(Item, {
791
+ id: itemId,
792
+ ref: ref,
793
+ children: [t1, t3]
794
+ })
785
795
  });
786
796
  $[5] = itemId;
787
797
  $[6] = ref;
@@ -813,10 +823,13 @@ const LoadingItem = /*#__PURE__*/React.forwardRef((t0, ref) => {
813
823
  }
814
824
  let t3;
815
825
  if ($[12] !== itemId || $[13] !== ref) {
816
- t3 = /*#__PURE__*/jsxs(Item, {
817
- id: itemId,
818
- ref: ref,
819
- children: [t1, t2]
826
+ t3 = /*#__PURE__*/jsx(LoadingPlaceholderContext.Provider, {
827
+ value: true,
828
+ children: /*#__PURE__*/jsxs(Item, {
829
+ id: itemId,
830
+ ref: ref,
831
+ children: [t1, t2]
832
+ })
820
833
  });
821
834
  $[12] = itemId;
822
835
  $[13] = ref;
@@ -1,5 +1,5 @@
1
- import './TreeView-0fe09f34.css';
1
+ import './TreeView-1bf45a33.css';
2
2
 
3
- var classes = {"TreeViewRootUlStyles":"prc-TreeView-TreeViewRootUlStyles-Mzrmj","TreeViewItem":"prc-TreeView-TreeViewItem-Ter5f","TreeViewItemContainer":"prc-TreeView-TreeViewItemContainer-z6qqQ","TreeViewItemSkeleton":"prc-TreeView-TreeViewItemSkeleton-FGlnx","TreeViewItemToggle":"prc-TreeView-TreeViewItemToggle-hq3Xq","TreeViewItemToggleHover":"prc-TreeView-TreeViewItemToggleHover-H9tbt","TreeViewItemToggleEnd":"prc-TreeView-TreeViewItemToggleEnd-nWt9I","TreeViewItemContent":"prc-TreeView-TreeViewItemContent-RKsCI","TreeViewItemContentText":"prc-TreeView-TreeViewItemContentText-FFaKp","TreeViewItemVisual":"prc-TreeView-TreeViewItemVisual-naWzj","TreeViewItemLeadingAction":"prc-TreeView-TreeViewItemLeadingAction-vmOIV","TreeViewItemTrailingAction":"prc-TreeView-TreeViewItemTrailingAction-h9M5g","TreeViewItemTrailingActionButton":"prc-TreeView-TreeViewItemTrailingActionButton-pCTdP","TreeViewItemLevelLine":"prc-TreeView-TreeViewItemLevelLine-F-0-2","TreeViewDirectoryIcon":"prc-TreeView-TreeViewDirectoryIcon-yP1oY","TreeViewVisuallyHidden":"prc-TreeView-TreeViewVisuallyHidden-1N8xK","TreeViewSkeletonItemContainerStyle":"prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd","TreeItemSkeletonTextStyles":"prc-TreeView-TreeItemSkeletonTextStyles-8VJ-g"};
3
+ var classes = {"TreeViewRootUlStyles":"prc-TreeView-TreeViewRootUlStyles-Mzrmj","TreeViewItem":"prc-TreeView-TreeViewItem-Ter5f","TreeViewItemContainer":"prc-TreeView-TreeViewItemContainer-z6qqQ","TreeViewItemToggle":"prc-TreeView-TreeViewItemToggle-hq3Xq","TreeViewItemToggleHover":"prc-TreeView-TreeViewItemToggleHover-H9tbt","TreeViewItemToggleEnd":"prc-TreeView-TreeViewItemToggleEnd-nWt9I","TreeViewItemContent":"prc-TreeView-TreeViewItemContent-RKsCI","TreeViewItemContentText":"prc-TreeView-TreeViewItemContentText-FFaKp","TreeViewItemVisual":"prc-TreeView-TreeViewItemVisual-naWzj","TreeViewItemLeadingAction":"prc-TreeView-TreeViewItemLeadingAction-vmOIV","TreeViewItemTrailingAction":"prc-TreeView-TreeViewItemTrailingAction-h9M5g","TreeViewItemTrailingActionButton":"prc-TreeView-TreeViewItemTrailingActionButton-pCTdP","TreeViewItemLevelLine":"prc-TreeView-TreeViewItemLevelLine-F-0-2","TreeViewDirectoryIcon":"prc-TreeView-TreeViewDirectoryIcon-yP1oY","TreeViewVisuallyHidden":"prc-TreeView-TreeViewVisuallyHidden-1N8xK","TreeViewSkeletonItemContainerStyle":"prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd","TreeItemSkeletonTextStyles":"prc-TreeView-TreeItemSkeletonTextStyles-8VJ-g"};
4
4
 
5
5
  export { classes as default };
@@ -3831,6 +3831,10 @@
3831
3831
  {
3832
3832
  "id": "experimental-components-keybindinghint-features--on-primary",
3833
3833
  "code": "(args) => (\n <div className={classes.PrimaryBackground}>\n <KeybindingHint {...args} />\n </div>\n)"
3834
+ },
3835
+ {
3836
+ "id": "experimental-components-keybindinghint-features--platforms",
3837
+ "code": "({ format = 'full', ...args }) => (\n <table className={classes.PlatformTable}>\n <thead>\n <tr>\n <th\n scope=\"col\"\n className={`${classes.PlatformCell} ${classes.PlatformHeader}`}\n >\n Platform\n </th>\n {modifierKeys.map((key) => (\n <th\n scope=\"col\"\n className={`${classes.PlatformCell} ${classes.PlatformHeader}`}\n key={key}\n >\n {key}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {platforms.map(({ platform, label }) => (\n <tr key={platform}>\n <th\n scope=\"row\"\n className={`${classes.PlatformCell} ${classes.PlatformHeader}`}\n >\n {label}\n </th>\n {modifierKeys.map((key) => (\n <td key={key} className={classes.PlatformCell}>\n <PlatformOverrideProvider value={platform}>\n <KeybindingHint {...args} format={format} keys={`${key}+K`} />\n </PlatformOverrideProvider>\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n)"
3834
3838
  }
3835
3839
  ],
3836
3840
  "importPath": "@primer/react",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@primer/react",
3
3
  "type": "module",
4
- "version": "38.27.0-rc.179829ffc",
4
+ "version": "38.27.0-rc.1c9d7ead9",
5
5
  "description": "An implementation of GitHub's Primer Design System using React",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -1,2 +0,0 @@
1
- .prc-TreeView-TreeViewRootUlStyles-Mzrmj{list-style:none;margin:0;padding:0}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f{outline:none}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f):focus-visible>div,:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f).focus-visible>div{box-shadow:var(--boxShadow-thick,inset 0 0 0 .125rem) var(--fgColor-accent,#0969da)}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f):focus-visible>div,:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f).focus-visible>div{outline:2px solid HighlightText;outline-offset:-2}}[data-has-leading-action]:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f){--has-leading-action:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ{--level:1;--toggle-width:1rem;--min-item-height:2rem;border-radius:var(--borderRadius-medium,.375rem);color:var(--fgColor-default,#1f2328);cursor:pointer;display:grid;font-size:var(--text-body-size-medium,.875rem);grid-template-areas:"spacer leadingAction toggle content trailingAction";grid-template-columns:var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;position:relative;width:100%;--leading-action-width:calc(var(--has-leading-action, 0)*1.5rem);--spacer-width:calc((var(--level) - 1)*(var(--toggle-width)/2))}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):hover{background-color:var(--control-transparent-bgColor-hover,#818b981a)}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):hover{outline:2px solid transparent;outline-offset:-2px}}@media (pointer:coarse){.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ{--toggle-width:1.5rem;--min-item-height:2.75rem}}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):has(.prc-TreeView-TreeViewItemSkeleton-FGlnx):hover{background-color:transparent;cursor:default}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContainer-z6qqQ):has(.prc-TreeView-TreeViewItemSkeleton-FGlnx):hover{outline:none}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-omit-spacer=true]) .prc-TreeView-TreeViewItemContainer-z6qqQ{grid-template-columns:0 0 0 1fr}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ{background-color:var(--control-transparent-bgColor-selected,#818b9826)}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ):after{background-color:var(--fgColor-accent,#0969da);border-radius:var(--borderRadius-medium,.375rem);content:"";height:1.5rem;left:calc(var(--base-size-8,.5rem)*-1);position:absolute;top:calc(50% - var(--base-size-12,.75rem));width:.25rem}@media (forced-colors:active){:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItem-Ter5f[aria-current=true]>.prc-TreeView-TreeViewItemContainer-z6qqQ):after{background-color:HighlightText}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggle-hq3Xq{align-items:flex-start;color:var(--fgColor-muted,#59636e);display:flex;grid-area:toggle;height:100%;justify-content:center;padding-top:calc(var(--min-item-height)/2 - var(--base-size-12,.75rem)/2)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggleHover-H9tbt:hover{background-color:var(--control-transparent-bgColor-hover,#818b981a)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemToggleEnd-nWt9I{border-bottom-left-radius:var(--borderRadius-medium,.375rem);border-top-left-radius:var(--borderRadius-medium,.375rem)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContent-RKsCI{display:flex;gap:var(--stack-gap-condensed,.5rem);grid-area:content;height:100%;line-height:var(--custom-line-height,var(--text-body-lineHeight-medium,1.4285));padding:0 var(--base-size-8,.5rem);padding-bottom:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2);padding-top:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemContentText-FFaKp{flex:1 1 auto;width:0}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-truncate-text=true]) .prc-TreeView-TreeViewItemContentText-FFaKp{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:where([data-truncate-text=false]) .prc-TreeView-TreeViewItemContentText-FFaKp{word-break:break-word}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemVisual-naWzj{align-items:center;color:var(--fgColor-muted,#59636e);display:flex;height:var(--custom-line-height,1.3rem)}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLeadingAction-vmOIV{color:var(--fgColor-muted,#59636e);display:flex;grid-area:leadingAction}:is(.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLeadingAction-vmOIV)>button{flex-shrink:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemTrailingAction-h9M5g{color:var(--fgColor-muted,#59636e);display:flex;grid-area:trailingAction}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemTrailingActionButton-pCTdP{flex-shrink:1}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLevelLine-F-0-2{border-color:var(--borderColor-muted,#d1d9e0b3);border-right:var(--borderWidth-thin,.0625rem) solid;height:100%;width:100%}@media (hover:hover){.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewItemLevelLine-F-0-2{border-color:transparent}.prc-TreeView-TreeViewRootUlStyles-Mzrmj:focus-within .prc-TreeView-TreeViewItemLevelLine-F-0-2,.prc-TreeView-TreeViewRootUlStyles-Mzrmj:hover .prc-TreeView-TreeViewItemLevelLine-F-0-2{border-color:var(--borderColor-muted,#d1d9e0b3)}}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewDirectoryIcon-yP1oY{color:var(--treeViewItem-leadingVisual-iconColor-rest,#54aeff);display:grid}.prc-TreeView-TreeViewRootUlStyles-Mzrmj .prc-TreeView-TreeViewVisuallyHidden-1N8xK{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd{align-items:center;column-gap:.5rem;display:flex;height:2rem}@media (pointer:coarse){.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd{height:2.75rem}}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+1){--tree-item-loading-width:67%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+2){--tree-item-loading-width:47%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+3){--tree-item-loading-width:73%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+4){--tree-item-loading-width:64%}.prc-TreeView-TreeViewSkeletonItemContainerStyle-lKqwd:nth-of-type(5n+5){--tree-item-loading-width:50%}.prc-TreeView-TreeItemSkeletonTextStyles-8VJ-g{width:var(--tree-item-loading-width,67%)}
2
- /*# sourceMappingURL=TreeView-0fe09f34.css.map */
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/TreeView/TreeView.module.css.js"],"names":[],"mappings":"AAAA,yCAGE,eAAgB,CADhB,QAAS,CADT,SAsPF,CArOE,0EACE,YAgBF,CAdE,kMAEE,mFAOF,CALE,8BAJF,kMAKI,+BAAgC,CAEhC,iBAEJ,CADE,CAGF,wGACE,sBACF,CAGF,mFACE,SAAU,CACV,mBAAoB,CACpB,sBAAuB,CAQvB,gDAAyC,CAFzC,oCAA6B,CAC7B,cAAe,CAJf,YAAa,CAEb,8CAAuC,CAKvC,wEAAyE,CADzE,6FAA8F,CAP9F,iBAAkB,CAElB,UAAW,CAQX,gEAAmE,CACnE,+DA8BF,CA5BE,8FACE,mEAMF,CAJE,8BAHF,8FAII,6BAA8B,CAC9B,mBAEJ,CADE,CAGF,wBA3BF,mFA4BI,qBAAsB,CACtB,yBAiBJ,CAhBE,CAQA,4IAEE,4BAA6B,CAD7B,cAMF,CAHE,8BAJF,4IAKI,YAEJ,CADE,CAIJ,kHACE,+BACF,CAEA,uIACE,sEAwBF,CApBE,kJAaE,8CAAuC,CACvC,gDAAyC,CARzC,UAAW,CADX,aAAc,CAFd,sCAAmC,CAFnC,iBAAkB,CAClB,0CAAoC,CAEpC,YAeF,CAHE,8BAhBF,kJAiBI,8BAEJ,CADE,CAIJ,gFAWE,sBAAuB,CAHvB,kCAA2B,CAP3B,YAAa,CAQb,gBAAiB,CAPjB,WAAY,CAQZ,sBAAuB,CAHvB,yEAKF,CAEA,2FACE,mEACF,CAEA,mFAEE,4DAAqD,CADrD,yDAEF,CAEA,iFACE,YAAa,CAWb,oCAA+B,CAD/B,iBAAkB,CATlB,WAAY,CAQZ,+EAAkF,CAPlF,kCAA6B,CAM7B,mFAAsF,CAFtF,gFAMF,CAEA,qFACE,aAAc,CACd,OACF,CAEA,sHACE,eAAgB,CAChB,sBAAuB,CACvB,kBACF,CAEA,uHAEE,qBACF,CAEA,gFAOE,kBAAmB,CADnB,kCAA2B,CAL3B,YAAa,CAIb,uCAGF,CAEA,uFAEE,kCAA2B,CAD3B,YAAa,CAEb,uBAKF,CAHE,mGACE,aACF,CAGF,wFAEE,kCAA2B,CAD3B,YAAa,CAEb,wBACF,CAEA,8FACE,aACF,CAEA,mFAQE,+CAAsC,CACtC,mDAA2C,CAP3C,WAAY,CADZ,UASF,CAQA,qBACE,mFACE,wBACF,CAEA,yLAEE,+CACF,CACF,CAEA,mFAEE,8DAAuD,CADvD,YAEF,CAEA,oFAGE,UAAW,CAGX,WAAY,CACZ,eAAgB,CAHhB,SAAU,CAHV,iBAAkB,CAClB,SAAU,CAOV,kBAAsB,CAEtB,cAAe,CADf,kBAEF,CAGF,uDAEE,kBAAmB,CACnB,gBAAkB,CAFlB,YAAa,CAGb,WAyBF,CAvBE,wBANF,uDAOI,cAsBJ,CArBE,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAEA,yEACE,6BACF,CAGF,+CACE,wCACF","file":"TreeView-0fe09f34.css","sourcesContent":[".TreeViewRootUlStyles {\n padding: 0;\n margin: 0;\n list-style: none;\n\n /*\n * WARNING: This is a performance optimization.\n *\n * We define styles for the tree items at the root level of the tree\n * to avoid recomputing the styles for each item when the tree updates.\n * We're sacrificing maintainability for performance because TreeView\n * needs to be performant enough to handle large trees (thousands of items).\n *\n * This is intended to be a temporary solution until we can improve the\n * performance of our styling patterns.\n *\n * Do NOT copy this pattern without understanding the tradeoffs.\n */\n .TreeViewItem {\n outline: none;\n\n &:focus-visible > div,\n &:global(.focus-visible) > div {\n box-shadow: var(--boxShadow-thick) var(--fgColor-accent);\n\n @media (forced-colors: active) {\n outline: 2px solid HighlightText;\n /* stylelint-disable-next-line declaration-property-value-no-unknown */\n outline-offset: -2;\n }\n }\n\n &[data-has-leading-action] {\n --has-leading-action: 1;\n }\n }\n\n .TreeViewItemContainer {\n --level: 1;\n --toggle-width: 1rem;\n --min-item-height: 2rem;\n\n position: relative;\n display: grid;\n width: 100%;\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-default);\n cursor: pointer;\n border-radius: var(--borderRadius-medium);\n grid-template-columns: var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;\n grid-template-areas: 'spacer leadingAction toggle content trailingAction';\n\n --leading-action-width: calc(var(--has-leading-action, 0) * 1.5rem);\n --spacer-width: calc(calc(var(--level) - 1) * (var(--toggle-width) / 2));\n\n &:hover {\n background-color: var(--control-transparent-bgColor-hover);\n\n @media (forced-colors: active) {\n outline: 2px solid transparent;\n outline-offset: -2px;\n }\n }\n\n @media (pointer: coarse) {\n --toggle-width: 1.5rem;\n --min-item-height: 2.75rem;\n }\n\n /*\n * NOTE: Uses descendant :has() - TreeViewItemSkeleton is nested inside\n * TreeViewItemContent > TreeViewItemContentText, not a direct child.\n * This is acceptable as the search is scoped to this element's subtree.\n */\n /* stylelint-disable-next-line selector-pseudo-class-disallowed-list -- scoped to CSS Module, audited (github/github-ui#17224) */\n &:has(.TreeViewItemSkeleton):hover {\n cursor: default;\n background-color: transparent;\n\n @media (forced-colors: active) {\n outline: none;\n }\n }\n }\n\n &:where([data-omit-spacer='true']) .TreeViewItemContainer {\n grid-template-columns: 0 0 0 1fr;\n }\n\n .TreeViewItem[aria-current='true'] > .TreeViewItemContainer {\n background-color: var(--control-transparent-bgColor-selected);\n\n /* Current item indicator */\n /* stylelint-disable-next-line selector-max-specificity */\n &::after {\n position: absolute;\n top: calc(50% - var(--base-size-12));\n left: calc(-1 * var(--base-size-8));\n width: 0.25rem;\n height: 1.5rem;\n content: '';\n\n /*\n * Use fgColor accent for consistency across all themes. Using the \"correct\" variable,\n * --bgColor-accent-emphasis, causes vrt failures for dark high contrast mode\n */\n /* stylelint-disable-next-line primer/colors */\n background-color: var(--fgColor-accent);\n border-radius: var(--borderRadius-medium);\n\n @media (forced-colors: active) {\n background-color: HighlightText;\n }\n }\n }\n\n .TreeViewItemToggle {\n display: flex;\n height: 100%;\n\n /* The toggle should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc(var(--min-item-height) / 2 - var(--base-size-12) / 2);\n color: var(--fgColor-muted);\n grid-area: toggle;\n justify-content: center;\n align-items: flex-start;\n }\n\n .TreeViewItemToggleHover:hover {\n background-color: var(--control-transparent-bgColor-hover);\n }\n\n .TreeViewItemToggleEnd {\n border-top-left-radius: var(--borderRadius-medium);\n border-bottom-left-radius: var(--borderRadius-medium);\n }\n\n .TreeViewItemContent {\n display: flex;\n height: 100%;\n padding: 0 var(--base-size-8);\n\n /* The dynamic top and bottom padding to maintain the minimum item height for single line items */\n /* stylelint-disable-next-line primer/spacing */\n padding-top: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n /* stylelint-disable-next-line primer/spacing */\n padding-bottom: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);\n line-height: var(--custom-line-height, var(--text-body-lineHeight-medium, 1.4285));\n grid-area: content;\n gap: var(--stack-gap-condensed);\n }\n\n .TreeViewItemContentText {\n flex: 1 1 auto;\n width: 0;\n }\n\n &:where([data-truncate-text='true']) .TreeViewItemContentText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &:where([data-truncate-text='false']) .TreeViewItemContentText {\n /* stylelint-disable-next-line declaration-property-value-keyword-no-deprecated */\n word-break: break-word;\n }\n\n .TreeViewItemVisual {\n display: flex;\n\n /* The visual icons should appear vertically centered for single-line items, but remain at the top for items that wrap\n across more lines. */\n height: var(--custom-line-height, 1.3rem);\n color: var(--fgColor-muted);\n align-items: center;\n }\n\n .TreeViewItemLeadingAction {\n display: flex;\n color: var(--fgColor-muted);\n grid-area: leadingAction;\n\n & > button {\n flex-shrink: 1;\n }\n }\n\n .TreeViewItemTrailingAction {\n display: flex;\n color: var(--fgColor-muted);\n grid-area: trailingAction;\n }\n\n .TreeViewItemTrailingActionButton {\n flex-shrink: 1;\n }\n\n .TreeViewItemLevelLine {\n width: 100%;\n height: 100%;\n\n /*\n * On devices without hover, the nesting indicator lines\n * appear at all times.\n */\n border-color: var(--borderColor-muted);\n border-right: var(--borderWidth-thin) solid;\n }\n\n /*\n * On devices with :hover support, the nesting indicator lines\n * fade in when the user mouses over the entire component,\n * or when there's focus inside the component. This makes\n * sure the component remains simple when not in use.\n */\n @media (hover: hover) {\n .TreeViewItemLevelLine {\n border-color: transparent;\n }\n\n &:hover .TreeViewItemLevelLine,\n &:focus-within .TreeViewItemLevelLine {\n border-color: var(--borderColor-muted);\n }\n }\n\n .TreeViewDirectoryIcon {\n display: grid;\n color: var(--treeViewItem-leadingVisual-iconColor-rest);\n }\n\n .TreeViewVisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n /* stylelint-disable-next-line primer/spacing */\n margin: -1px;\n overflow: hidden;\n /* stylelint-disable-next-line property-no-deprecated */\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n }\n}\n\n.TreeViewSkeletonItemContainerStyle {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n height: 2rem;\n\n @media (pointer: coarse) {\n height: 2.75rem;\n }\n\n &:nth-of-type(5n + 1) {\n --tree-item-loading-width: 67%;\n }\n\n &:nth-of-type(5n + 2) {\n --tree-item-loading-width: 47%;\n }\n\n &:nth-of-type(5n + 3) {\n --tree-item-loading-width: 73%;\n }\n\n &:nth-of-type(5n + 4) {\n --tree-item-loading-width: 64%;\n }\n\n &:nth-of-type(5n + 5) {\n --tree-item-loading-width: 50%;\n }\n}\n\n.TreeItemSkeletonTextStyles {\n width: var(--tree-item-loading-width, 67%);\n}\n"]}
@@ -1,32 +0,0 @@
1
- import { isMacOS } from '@primer/behaviors/utils';
2
- import { useSyncExternalStore } from 'react';
3
-
4
- // No-op. The platform never changes at runtime, so there is nothing to
5
- // subscribe to. Hoisted to avoid creating a new function on every call.
6
- const subscribe = () => () => {};
7
-
8
- // Safe default for SSR since we can't detect the platform on the server.
9
- const getServerSnapshot = () => false;
10
-
11
- /**
12
- * SSR-safe hook for determining if the current platform is MacOS.
13
- *
14
- * Uses `useSyncExternalStore` to read the platform value:
15
- *
16
- * - On the **client**, `ssrUnsafeIsMacOS` reads `navigator.userAgent` and
17
- * returns the real value immediately, with no extra render pass.
18
- *
19
- * - On the **server**, returns `false`. During hydration, if the snapshots
20
- * differ, React handles the mismatch internally in a single synchronous
21
- * pass, avoiding the layout shift that a deferred `useEffect` + `setState`
22
- * would cause.
23
- *
24
- * Previous implementation used `useState` + `useEffect`, which caused an
25
- * unconditional second render on every mount (even on the client where the
26
- * initial value was already correct).
27
- */
28
- function useIsMacOS() {
29
- return useSyncExternalStore(subscribe, isMacOS, getServerSnapshot);
30
- }
31
-
32
- export { useIsMacOS };