@astryxdesign/core 0.5.0-canary.c47e754 → 0.5.0-canary.ea888ef
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/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +26 -2
- package/dist/hooks/useHotkeys.d.ts.map +1 -1
- package/dist/hooks/useHotkeys.js +25 -3
- package/dist/theme/syntax/tokens.d.ts +1 -1
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +2 -5
- package/package.json +2 -2
- package/src/Kbd/Kbd.test.tsx +17 -0
- package/src/Kbd/Kbd.tsx +26 -2
- package/src/hooks/useHotkeys.test.ts +18 -0
- package/src/hooks/useHotkeys.ts +25 -3
- package/src/theme/syntax/tokens.ts +2 -5
- package/dist/utils/isApplePlatform.d.ts +0 -18
- package/dist/utils/isApplePlatform.d.ts.map +0 -1
- package/dist/utils/isApplePlatform.js +0 -37
- package/src/utils/isApplePlatform.test.ts +0 -61
- package/src/utils/isApplePlatform.ts +0 -37
package/dist/Kbd/Kbd.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Kbd.d.ts","sourceRoot":"","sources":["../../src/Kbd/Kbd.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAA6B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Kbd.d.ts","sourceRoot":"","sources":["../../src/Kbd/Kbd.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAA6B,MAAM,OAAO,CAAC;AAGlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAmI5C,MAAM,WAAW,QAAS,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC1D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,GAAG,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAC,EAAE,QAAQ,qBAgC3E;yBAhCe,GAAG"}
|
package/dist/Kbd/Kbd.js
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import React, { useSyncExternalStore } from 'react';
|
|
16
16
|
import * as stylex from '@stylexjs/stylex';
|
|
17
17
|
import { mergeProps } from "../utils/index.js";
|
|
18
|
-
import { isApplePlatform } from "../utils/isApplePlatform.js";
|
|
19
18
|
import { themeProps } from "../utils/themeProps.js";
|
|
20
19
|
import "../theme/tokens.stylex.js";
|
|
21
20
|
import { colorVars, spacingVars, radiusVars, typographyVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
@@ -99,6 +98,31 @@ function subscribeToPlatformChanges() {
|
|
|
99
98
|
function getServerPlatformSnapshot() {
|
|
100
99
|
return false;
|
|
101
100
|
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Detects whether the current platform is macOS/iOS.
|
|
104
|
+
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
105
|
+
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
106
|
+
* supported) when it is absent or blank.
|
|
107
|
+
*/
|
|
108
|
+
function detectMac() {
|
|
109
|
+
if (typeof navigator === 'undefined') {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
// Prefer User-Agent Client Hints API (not deprecated)
|
|
113
|
+
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
114
|
+
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
115
|
+
const uaPlatform = uaData.platform;
|
|
116
|
+
// A blank platform is no answer, not a negative one. Builds that rewrite
|
|
117
|
+
// their client-hints identity ship '', so fall through rather than
|
|
118
|
+
// reading it as "not Apple".
|
|
119
|
+
if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
|
|
120
|
+
return /mac/i.test(uaPlatform);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Fallback: navigator.platform (deprecated but still shipped everywhere)
|
|
124
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
125
|
+
}
|
|
102
126
|
/**
|
|
103
127
|
* Displays a keyboard shortcut as styled <kbd> elements.
|
|
104
128
|
*
|
|
@@ -122,7 +146,7 @@ export function Kbd({
|
|
|
122
146
|
style,
|
|
123
147
|
...rest
|
|
124
148
|
}) {
|
|
125
|
-
const isMac = useSyncExternalStore(subscribeToPlatformChanges,
|
|
149
|
+
const isMac = useSyncExternalStore(subscribeToPlatformChanges, detectMac, getServerPlatformSnapshot);
|
|
126
150
|
const parts = keys.split('+').map(key => key.trim().toLowerCase());
|
|
127
151
|
|
|
128
152
|
// Screen-reader name: the joined spoken labels (e.g. "Command + K"), since
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../../src/hooks/useHotkeys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../../src/hooks/useHotkeys.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;;;;;;;;;;;;;;OAeG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkGD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAkClD"}
|
package/dist/hooks/useHotkeys.js
CHANGED
|
@@ -18,15 +18,13 @@
|
|
|
18
18
|
* unless a hotkey opts in via `allowInInputs`.
|
|
19
19
|
*
|
|
20
20
|
* Platform-aware: `mod` maps to metaKey (⌘) on Apple platforms and
|
|
21
|
-
* ctrlKey elsewhere
|
|
22
|
-
* displayed and handled shortcuts cannot disagree.
|
|
21
|
+
* ctrlKey elsewhere — mirrors the detection used by Kbd.
|
|
23
22
|
*
|
|
24
23
|
* SYNC: When modified, update:
|
|
25
24
|
* - /packages/core/src/hooks/index.ts
|
|
26
25
|
* - /packages/core/src/hooks/useHotkeys.doc.mjs
|
|
27
26
|
*/
|
|
28
27
|
import { useEffect, useRef } from 'react';
|
|
29
|
-
import { isApplePlatform } from "../utils/isApplePlatform.js";
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
30
|
* A single keyboard shortcut registration.
|
|
@@ -44,6 +42,30 @@ const KEY_ALIASES = {
|
|
|
44
42
|
plus: '+'
|
|
45
43
|
};
|
|
46
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Detects whether the current platform is macOS/iOS.
|
|
47
|
+
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
48
|
+
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
49
|
+
* supported) when it is absent or blank.
|
|
50
|
+
* Mirrors the detection used by Kbd so displayed and handled shortcuts agree.
|
|
51
|
+
*/
|
|
52
|
+
function isApplePlatform() {
|
|
53
|
+
if (typeof navigator === 'undefined') {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
57
|
+
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
58
|
+
const uaPlatform = uaData.platform;
|
|
59
|
+
// A blank platform is no answer, not a negative one. Builds that rewrite
|
|
60
|
+
// their client-hints identity ship '', so fall through rather than
|
|
61
|
+
// reading it as "not Apple".
|
|
62
|
+
if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
|
|
63
|
+
return /mac/i.test(uaPlatform);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
/**
|
|
48
70
|
* Whether the event target is a typing surface where global shortcuts
|
|
49
71
|
* should stay silent by default.
|
|
@@ -29,7 +29,7 @@ export declare const syntaxTokenDefaults: {
|
|
|
29
29
|
readonly '--color-syntax-tag': "var(--color-text-red)";
|
|
30
30
|
readonly '--color-syntax-attribute': "var(--color-text-teal)";
|
|
31
31
|
readonly '--color-syntax-property': "var(--color-text-cyan)";
|
|
32
|
-
readonly '--color-syntax-punctuation': "var(--color-text-
|
|
32
|
+
readonly '--color-syntax-punctuation': "var(--color-text-disabled)";
|
|
33
33
|
readonly '--color-syntax-background': "var(--color-background-muted)";
|
|
34
34
|
};
|
|
35
35
|
export type SyntaxTokenName = keyof typeof syntaxTokenDefaults;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/theme/syntax/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/theme/syntax/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;CA6BtB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,mBAAmB,CAAC"}
|
|
@@ -44,11 +44,8 @@ export const syntaxTokenDefaults = {
|
|
|
44
44
|
'--color-syntax-attribute': 'var(--color-text-teal)',
|
|
45
45
|
// property -> cyan text (object properties)
|
|
46
46
|
'--color-syntax-property': 'var(--color-text-cyan)',
|
|
47
|
-
// punctuation ->
|
|
48
|
-
|
|
49
|
-
// controls), and punctuation in a code sample is normal, always-active
|
|
50
|
-
// text — see #5386.
|
|
51
|
-
'--color-syntax-punctuation': 'var(--color-text-secondary)',
|
|
47
|
+
// punctuation -> disabled text (brackets, semicolons)
|
|
48
|
+
'--color-syntax-punctuation': 'var(--color-text-disabled)',
|
|
52
49
|
// background -> muted surface
|
|
53
50
|
'--color-syntax-background': 'var(--color-background-muted)'
|
|
54
51
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.5.0-canary.
|
|
3
|
+
"version": "0.5.0-canary.ea888ef",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -661,7 +661,7 @@
|
|
|
661
661
|
"react-dom": ">=19.0.0"
|
|
662
662
|
},
|
|
663
663
|
"devDependencies": {
|
|
664
|
-
"@astryxdesign/cli": "0.5.0-canary.
|
|
664
|
+
"@astryxdesign/cli": "0.5.0-canary.ea888ef",
|
|
665
665
|
"@babel/cli": "^8.0.4",
|
|
666
666
|
"@babel/core": "^7.29.7",
|
|
667
667
|
"@babel/preset-react": "^8.0.1",
|
package/src/Kbd/Kbd.test.tsx
CHANGED
|
@@ -23,6 +23,7 @@ describe('Kbd', () => {
|
|
|
23
23
|
value: originalPlatform,
|
|
24
24
|
configurable: true,
|
|
25
25
|
});
|
|
26
|
+
delete (navigator as {userAgentData?: unknown}).userAgentData;
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
it('renders a single key', () => {
|
|
@@ -54,6 +55,22 @@ describe('Kbd', () => {
|
|
|
54
55
|
expect(screen.getByText('\u2318')).toBeInTheDocument(); // \u2318
|
|
55
56
|
});
|
|
56
57
|
|
|
58
|
+
it('reads a blank userAgentData.platform as unknown, not as non-Mac', () => {
|
|
59
|
+
// Builds that rewrite their client-hints identity expose the key with an
|
|
60
|
+
// empty value; navigator.platform is the only surface left that answers.
|
|
61
|
+
Object.defineProperty(navigator, 'userAgentData', {
|
|
62
|
+
value: {platform: ''},
|
|
63
|
+
configurable: true,
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(navigator, 'platform', {
|
|
66
|
+
value: 'MacIntel',
|
|
67
|
+
configurable: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
render(<Kbd keys="mod" />);
|
|
71
|
+
expect(screen.getByText('\u2318')).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
57
74
|
it('maps modifier keys to symbols', () => {
|
|
58
75
|
render(<Kbd keys="ctrl+alt+shift+k" />);
|
|
59
76
|
expect(screen.getByText('\u2303')).toBeInTheDocument(); // \u2303
|
package/src/Kbd/Kbd.tsx
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
import React, {useSyncExternalStore} from 'react';
|
|
17
17
|
import * as stylex from '@stylexjs/stylex';
|
|
18
18
|
import {mergeProps} from '../utils';
|
|
19
|
-
import {isApplePlatform} from '../utils/isApplePlatform';
|
|
20
19
|
import type {BaseProps} from '../BaseProps';
|
|
21
20
|
import {themeProps} from '../utils/themeProps';
|
|
22
21
|
import {
|
|
@@ -123,6 +122,31 @@ function getServerPlatformSnapshot(): boolean {
|
|
|
123
122
|
return false;
|
|
124
123
|
}
|
|
125
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Detects whether the current platform is macOS/iOS.
|
|
127
|
+
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
128
|
+
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
129
|
+
* supported) when it is absent or blank.
|
|
130
|
+
*/
|
|
131
|
+
function detectMac(): boolean {
|
|
132
|
+
if (typeof navigator === 'undefined') {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// Prefer User-Agent Client Hints API (not deprecated)
|
|
136
|
+
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
137
|
+
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
138
|
+
const uaPlatform = (uaData as {platform?: unknown}).platform;
|
|
139
|
+
// A blank platform is no answer, not a negative one. Builds that rewrite
|
|
140
|
+
// their client-hints identity ship '', so fall through rather than
|
|
141
|
+
// reading it as "not Apple".
|
|
142
|
+
if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
|
|
143
|
+
return /mac/i.test(uaPlatform);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Fallback: navigator.platform (deprecated but still shipped everywhere)
|
|
147
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
148
|
+
}
|
|
149
|
+
|
|
126
150
|
export interface KbdProps extends BaseProps<HTMLSpanElement> {
|
|
127
151
|
ref?: React.Ref<HTMLSpanElement>;
|
|
128
152
|
/**
|
|
@@ -159,7 +183,7 @@ export interface KbdProps extends BaseProps<HTMLSpanElement> {
|
|
|
159
183
|
export function Kbd({keys, ref, xstyle, className, style, ...rest}: KbdProps) {
|
|
160
184
|
const isMac = useSyncExternalStore(
|
|
161
185
|
subscribeToPlatformChanges,
|
|
162
|
-
|
|
186
|
+
detectMac,
|
|
163
187
|
getServerPlatformSnapshot,
|
|
164
188
|
);
|
|
165
189
|
|
|
@@ -67,6 +67,24 @@ describe('useHotkeys', () => {
|
|
|
67
67
|
expect(onPress).toHaveBeenCalledTimes(1);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
it('reads a blank userAgentData.platform as unknown, not as non-Apple', () => {
|
|
71
|
+
// Builds that rewrite their client-hints identity expose the key with an
|
|
72
|
+
// empty value; navigator.platform is the only surface left that answers.
|
|
73
|
+
vi.stubGlobal('navigator', {
|
|
74
|
+
userAgentData: {platform: ''},
|
|
75
|
+
platform: 'MacIntel',
|
|
76
|
+
});
|
|
77
|
+
const onPress = vi.fn();
|
|
78
|
+
renderHook(() => useHotkeys([{keys: 'mod+k', onPress}]));
|
|
79
|
+
|
|
80
|
+
press('k', {ctrlKey: true});
|
|
81
|
+
expect(onPress).not.toHaveBeenCalled();
|
|
82
|
+
|
|
83
|
+
const event = press('k', {metaKey: true});
|
|
84
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
85
|
+
expect(onPress).toHaveBeenCalledWith(event);
|
|
86
|
+
});
|
|
87
|
+
|
|
70
88
|
it('does not fire a bare key when modifiers are held', () => {
|
|
71
89
|
stubApplePlatform();
|
|
72
90
|
const onPress = vi.fn();
|
package/src/hooks/useHotkeys.ts
CHANGED
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
* unless a hotkey opts in via `allowInInputs`.
|
|
19
19
|
*
|
|
20
20
|
* Platform-aware: `mod` maps to metaKey (⌘) on Apple platforms and
|
|
21
|
-
* ctrlKey elsewhere
|
|
22
|
-
* displayed and handled shortcuts cannot disagree.
|
|
21
|
+
* ctrlKey elsewhere — mirrors the detection used by Kbd.
|
|
23
22
|
*
|
|
24
23
|
* SYNC: When modified, update:
|
|
25
24
|
* - /packages/core/src/hooks/index.ts
|
|
@@ -27,7 +26,6 @@
|
|
|
27
26
|
*/
|
|
28
27
|
|
|
29
28
|
import {useEffect, useRef} from 'react';
|
|
30
|
-
import {isApplePlatform} from '../utils/isApplePlatform';
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
31
|
* A single keyboard shortcut registration.
|
|
@@ -73,6 +71,30 @@ const KEY_ALIASES: Record<string, string> = {
|
|
|
73
71
|
plus: '+',
|
|
74
72
|
};
|
|
75
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Detects whether the current platform is macOS/iOS.
|
|
76
|
+
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
77
|
+
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
78
|
+
* supported) when it is absent or blank.
|
|
79
|
+
* Mirrors the detection used by Kbd so displayed and handled shortcuts agree.
|
|
80
|
+
*/
|
|
81
|
+
function isApplePlatform(): boolean {
|
|
82
|
+
if (typeof navigator === 'undefined') {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
86
|
+
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
87
|
+
const uaPlatform = (uaData as {platform?: unknown}).platform;
|
|
88
|
+
// A blank platform is no answer, not a negative one. Builds that rewrite
|
|
89
|
+
// their client-hints identity ship '', so fall through rather than
|
|
90
|
+
// reading it as "not Apple".
|
|
91
|
+
if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
|
|
92
|
+
return /mac/i.test(uaPlatform);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
96
|
+
}
|
|
97
|
+
|
|
76
98
|
/**
|
|
77
99
|
* Whether the event target is a typing surface where global shortcuts
|
|
78
100
|
* should stay silent by default.
|
|
@@ -44,11 +44,8 @@ export const syntaxTokenDefaults = {
|
|
|
44
44
|
'--color-syntax-attribute': 'var(--color-text-teal)',
|
|
45
45
|
// property -> cyan text (object properties)
|
|
46
46
|
'--color-syntax-property': 'var(--color-text-cyan)',
|
|
47
|
-
// punctuation ->
|
|
48
|
-
|
|
49
|
-
// controls), and punctuation in a code sample is normal, always-active
|
|
50
|
-
// text — see #5386.
|
|
51
|
-
'--color-syntax-punctuation': 'var(--color-text-secondary)',
|
|
47
|
+
// punctuation -> disabled text (brackets, semicolons)
|
|
48
|
+
'--color-syntax-punctuation': 'var(--color-text-disabled)',
|
|
52
49
|
// background -> muted surface
|
|
53
50
|
'--color-syntax-background': 'var(--color-background-muted)',
|
|
54
51
|
} as const;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file isApplePlatform.ts
|
|
3
|
-
* @input Reads navigator.userAgentData.platform and navigator.platform
|
|
4
|
-
* @output Exports isApplePlatform
|
|
5
|
-
* @position Internal util; the single platform detection behind useHotkeys and Kbd
|
|
6
|
-
*
|
|
7
|
-
* Deliberately absent from utils/index.ts: packages/core/src/index.ts does
|
|
8
|
-
* `export * from './utils'`, so naming it there would publish it as API.
|
|
9
|
-
* Import it by path, as interactionModality is imported.
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Detects whether the current platform is macOS/iOS.
|
|
13
|
-
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
14
|
-
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
15
|
-
* supported) when it names none.
|
|
16
|
-
*/
|
|
17
|
-
export declare function isApplePlatform(): boolean;
|
|
18
|
-
//# sourceMappingURL=isApplePlatform.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isApplePlatform.d.ts","sourceRoot":"","sources":["../../src/utils/isApplePlatform.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAiBzC"}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file isApplePlatform.ts
|
|
5
|
-
* @input Reads navigator.userAgentData.platform and navigator.platform
|
|
6
|
-
* @output Exports isApplePlatform
|
|
7
|
-
* @position Internal util; the single platform detection behind useHotkeys and Kbd
|
|
8
|
-
*
|
|
9
|
-
* Deliberately absent from utils/index.ts: packages/core/src/index.ts does
|
|
10
|
-
* `export * from './utils'`, so naming it there would publish it as API.
|
|
11
|
-
* Import it by path, as interactionModality is imported.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Detects whether the current platform is macOS/iOS.
|
|
16
|
-
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
17
|
-
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
18
|
-
* supported) when it names none.
|
|
19
|
-
*/
|
|
20
|
-
export function isApplePlatform() {
|
|
21
|
-
if (typeof navigator === 'undefined') {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
25
|
-
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
26
|
-
const uaPlatform = uaData.platform;
|
|
27
|
-
const named = typeof uaPlatform === 'string' ? uaPlatform.trim() : '';
|
|
28
|
-
// A value that names nothing is no answer, not a negative one: builds that
|
|
29
|
-
// rewrite their client-hints identity ship '', and 'Unknown' is the spec's
|
|
30
|
-
// own sentinel for "cannot say". Both fall through rather than reading as
|
|
31
|
-
// "not Apple".
|
|
32
|
-
if (named !== '' && named.toLowerCase() !== 'unknown') {
|
|
33
|
-
return /mac/i.test(named);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
37
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file isApplePlatform.test.ts
|
|
5
|
-
* @input Uses vitest, isApplePlatform
|
|
6
|
-
* @output Unit tests for isApplePlatform
|
|
7
|
-
* @position Testing; validates isApplePlatform.ts implementation
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import {describe, it, expect, vi, afterEach} from 'vitest';
|
|
11
|
-
import {isApplePlatform} from './isApplePlatform';
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
vi.unstubAllGlobals();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('isApplePlatform', () => {
|
|
18
|
-
it('trusts a client-hints platform that names one', () => {
|
|
19
|
-
vi.stubGlobal('navigator', {
|
|
20
|
-
userAgentData: {platform: 'macOS'},
|
|
21
|
-
platform: 'Win32',
|
|
22
|
-
});
|
|
23
|
-
expect(isApplePlatform()).toBe(true);
|
|
24
|
-
|
|
25
|
-
vi.stubGlobal('navigator', {
|
|
26
|
-
userAgentData: {platform: 'Windows'},
|
|
27
|
-
platform: 'MacIntel',
|
|
28
|
-
});
|
|
29
|
-
expect(isApplePlatform()).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it.each([
|
|
33
|
-
['blank', ''],
|
|
34
|
-
['whitespace', ' '],
|
|
35
|
-
['the spec Unknown sentinel', 'Unknown'],
|
|
36
|
-
['unknown in any case', 'UNKNOWN'],
|
|
37
|
-
['a non-string', null],
|
|
38
|
-
])('falls through to navigator.platform on %s', (_label, platform) => {
|
|
39
|
-
vi.stubGlobal('navigator', {
|
|
40
|
-
userAgentData: {platform},
|
|
41
|
-
platform: 'MacIntel',
|
|
42
|
-
});
|
|
43
|
-
expect(isApplePlatform()).toBe(true);
|
|
44
|
-
|
|
45
|
-
vi.stubGlobal('navigator', {userAgentData: {platform}, platform: 'Win32'});
|
|
46
|
-
expect(isApplePlatform()).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('falls back to navigator.platform when client hints are absent', () => {
|
|
50
|
-
vi.stubGlobal('navigator', {platform: 'iPhone'});
|
|
51
|
-
expect(isApplePlatform()).toBe(true);
|
|
52
|
-
|
|
53
|
-
vi.stubGlobal('navigator', {platform: 'Linux x86_64'});
|
|
54
|
-
expect(isApplePlatform()).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('answers false when there is no navigator at all', () => {
|
|
58
|
-
vi.stubGlobal('navigator', undefined);
|
|
59
|
-
expect(isApplePlatform()).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file isApplePlatform.ts
|
|
5
|
-
* @input Reads navigator.userAgentData.platform and navigator.platform
|
|
6
|
-
* @output Exports isApplePlatform
|
|
7
|
-
* @position Internal util; the single platform detection behind useHotkeys and Kbd
|
|
8
|
-
*
|
|
9
|
-
* Deliberately absent from utils/index.ts: packages/core/src/index.ts does
|
|
10
|
-
* `export * from './utils'`, so naming it there would publish it as API.
|
|
11
|
-
* Import it by path, as interactionModality is imported.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Detects whether the current platform is macOS/iOS.
|
|
16
|
-
* Prefers the User-Agent Client Hints API when it names a platform (modern
|
|
17
|
-
* Chrome/Edge), falls back to navigator.platform (deprecated but universally
|
|
18
|
-
* supported) when it names none.
|
|
19
|
-
*/
|
|
20
|
-
export function isApplePlatform(): boolean {
|
|
21
|
-
if (typeof navigator === 'undefined') {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
|
|
25
|
-
if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
|
|
26
|
-
const uaPlatform = (uaData as {platform?: unknown}).platform;
|
|
27
|
-
const named = typeof uaPlatform === 'string' ? uaPlatform.trim() : '';
|
|
28
|
-
// A value that names nothing is no answer, not a negative one: builds that
|
|
29
|
-
// rewrite their client-hints identity ship '', and 'Unknown' is the spec's
|
|
30
|
-
// own sentinel for "cannot say". Both fall through rather than reading as
|
|
31
|
-
// "not Apple".
|
|
32
|
-
if (named !== '' && named.toLowerCase() !== 'unknown') {
|
|
33
|
-
return /mac/i.test(named);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
|
|
37
|
-
}
|