Skip to content

Commit 650399a

Browse files
chore: Refactor visual mode hooks (#171)
1 parent aebb97f commit 650399a

File tree

1 file changed

+40
-40
lines changed

1 file changed

+40
-40
lines changed

src/internal/visual-mode/index.ts

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,16 @@ export function isMotionDisabled(element: HTMLElement): boolean {
2626
);
2727
}
2828

29-
// Note that this hook doesn't take into consideration @media print (unlike the dark mode CSS),
30-
// due to challenges with cross-browser implementations of media/print state change listeners.
31-
// This means that components using this hook will render in dark mode even when printing.
32-
export function useCurrentMode(elementRef: React.RefObject<HTMLElement>) {
33-
const [value, setValue] = useState<'light' | 'dark'>('light');
34-
useMutationObserver(elementRef, node => {
35-
const darkModeParent = findUpUntil(
36-
node,
37-
node => node.classList.contains('awsui-polaris-dark-mode') || node.classList.contains('awsui-dark-mode')
38-
);
39-
const newValue = darkModeParent ? 'dark' : 'light';
40-
41-
// refer to the comment below in `useReducedMotion`
42-
if (newValue !== value) {
43-
setValue(newValue);
44-
}
45-
});
46-
return value;
47-
}
48-
49-
export function useDensityMode(elementRef: React.RefObject<HTMLElement>) {
50-
const [value, setValue] = useState<'comfortable' | 'compact'>('comfortable');
51-
useMutationObserver(elementRef, node => {
52-
const compactModeParent = findUpUntil(
53-
node,
54-
node => node.classList.contains('awsui-polaris-compact-mode') || node.classList.contains('awsui-compact-mode')
55-
);
56-
const newValue = compactModeParent ? 'compact' : 'comfortable';
57-
58-
// refer to the comment below in `useReducedMotion`
59-
if (newValue !== value) {
60-
setValue(newValue);
61-
}
62-
});
63-
return value;
64-
}
65-
66-
export function useReducedMotion(elementRef: React.RefObject<HTMLElement>) {
67-
const [value, setValue] = useState(false);
29+
// Generic hook for detecting mode changes via DOM mutation observation.
30+
// Prevents unnecessary re-renders by only updating state when the value actually changes.
31+
function useModeDetector<T>(
32+
elementRef: React.RefObject<HTMLElement>,
33+
detector: (node: HTMLElement) => T,
34+
initialValue: T
35+
): T {
36+
const [value, setValue] = useState<T>(initialValue);
6837
useMutationObserver(elementRef, node => {
69-
const newValue = isMotionDisabled(node);
38+
const newValue = detector(node);
7039
/**
7140
* React has a behavior that triggers a re-render even if the same value is provided in the setState, while it does not
7241
* commit any changes to the DOM (commit phase) the function rerenders. This causes a false react act warnings in testing
@@ -82,6 +51,37 @@ export function useReducedMotion(elementRef: React.RefObject<HTMLElement>) {
8251
return value;
8352
}
8453

54+
function detectCurrentMode(node: HTMLElement): 'light' | 'dark' {
55+
const darkModeParent = findUpUntil(
56+
node,
57+
node => node.classList.contains('awsui-polaris-dark-mode') || node.classList.contains('awsui-dark-mode')
58+
);
59+
return darkModeParent ? 'dark' : 'light';
60+
}
61+
62+
function detectDensityMode(node: HTMLElement): 'comfortable' | 'compact' {
63+
const compactModeParent = findUpUntil(
64+
node,
65+
node => node.classList.contains('awsui-polaris-compact-mode') || node.classList.contains('awsui-compact-mode')
66+
);
67+
return compactModeParent ? 'compact' : 'comfortable';
68+
}
69+
70+
// Note that this hook doesn't take into consideration @media print (unlike the dark mode CSS),
71+
// due to challenges with cross-browser implementations of media/print state change listeners.
72+
// This means that components using this hook will render in dark mode even when printing.
73+
export function useCurrentMode(elementRef: React.RefObject<HTMLElement>) {
74+
return useModeDetector(elementRef, detectCurrentMode, 'light');
75+
}
76+
77+
export function useDensityMode(elementRef: React.RefObject<HTMLElement>) {
78+
return useModeDetector(elementRef, detectDensityMode, 'comfortable');
79+
}
80+
81+
export function useReducedMotion(elementRef: React.RefObject<HTMLElement>) {
82+
return useModeDetector(elementRef, isMotionDisabled, false);
83+
}
84+
8585
const useMutationSingleton = createSingletonHandler<void>(handler => {
8686
const observer = new MutationObserver(() => handler());
8787
observer.observe(document.body, { attributes: true, subtree: true });

0 commit comments

Comments
 (0)