-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.ts
136 lines (121 loc) · 4.15 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { REGISTERED_SUBTHEMES as Subthemes } from './RegisteredSubthemes';
import RegisteredSubthemeType from './Subtheme';
import Storage from '../utils/Storage';
import Config from '../Config';
// Expose Subthemes publicly
export { Subthemes };
export type { RegisteredSubthemeType };
/**
* Updates the appearance of the page based on the Subtheme details to be
* changed. If no Subtheme details are specified, the method uses Subtheme info
* from Storage.
*
* This method also persists Subtheme changes to Storage (unless
* `persistUpdate` is set to `false`.)
*
* @param newSubtheme The subtheme details to be updated. This defaults to
* the subtheme from Storage.
* @param persistUpdate Defaults to true. If set to false, the updated theme
* is not persisted to Storage.
*/
export function updateTheme(
{ name, mode }: Partial<SubthemeSelectionType> = {},
persistUpdate = true,
): void {
const {
name: stored_subtheme_name,
mode: stored_subtheme_mode,
} = getStoredSubtheme();
const normalized_name = verifySubthemeName(name ?? stored_subtheme_name);
const currently_selected_mode = verifySubthemeMode(
mode ?? stored_subtheme_mode,
);
const normalized_mode = normalizeSubthemeMode(currently_selected_mode);
// First store changes. Then decide if we need to take any action on the DOM.
if (persistUpdate) {
storeSubtheme({ name: normalized_name, mode: currently_selected_mode });
}
if (
normalized_name === stored_subtheme_name &&
normalized_mode === stored_subtheme_mode &&
// If stores are not updated, the "current" subthemes from the store may be
// stale. Hence, skip this optimization.
persistUpdate
) {
return;
}
const old_subtheme = Subthemes[stored_subtheme_name];
const new_subtheme = Subthemes[normalized_name];
old_subtheme.reset(normalizeSubthemeMode(stored_subtheme_mode));
new_subtheme.apply(normalized_mode);
}
// Make this method accessible to the plugins.
window.PrimerSpec.updateTheme = updateTheme;
/**
* Retrieve the previously stored subtheme name from persistent local
* storage. If this cannot be retrieved, returns the name of the first
* available subtheme.
*/
export function getStoredSubthemeName(): string {
const stored_subtheme_name = Storage.get(Config.SUBTHEME_NAME_STORAGE_KEY);
return verifySubthemeName(stored_subtheme_name ?? Config.INIT_SUBTHEME_NAME);
}
/**
* Retrieve the previously stored subtheme mode from persistent local
* storage. If this cannot be retrieved, returns the default mode.
*/
export function getStoredSubthemeMode(): SubthemeModeSelectorType {
const stored_subtheme_mode = Storage.get(Config.SUBTHEME_MODE_STORAGE_KEY);
return verifySubthemeMode(stored_subtheme_mode ?? Config.INIT_SUBTHEME_MODE);
}
export function normalizeSubthemeMode(
mode: SubthemeModeSelectorType,
): SubthemeModeType {
if (mode !== 'system') {
return mode;
}
if (!window.matchMedia) {
return 'light';
}
// The following is based on:
// https://gosink.in/javascript-css-toggle-dark-light-theme-based-on-your-users-preferred-scheme/
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
// It's a dark theme
return 'dark';
}
// Otherwise, it's not a dark theme
return 'light';
}
/**
* Update persistent local storage with the given subtheme for future
* retrieval.
* @param subtheme the name to be stored in local storage
*/
function storeSubtheme({ name, mode }: SubthemeSelectionType) {
Storage.set(Config.SUBTHEME_NAME_STORAGE_KEY, name);
Storage.set(Config.SUBTHEME_MODE_STORAGE_KEY, mode);
}
function getStoredSubtheme() {
return { name: getStoredSubthemeName(), mode: getStoredSubthemeMode() };
}
function verifySubthemeName(name: string) {
if (name && Subthemes[name]) {
return name;
}
console.warn(
`Primer Spec: Invalid subtheme name: ${name}. Reverting to 'default'`,
);
return Subthemes.default.name;
}
function verifySubthemeMode(mode: string | null) {
switch (mode) {
case 'light':
case 'dark':
case 'system':
return mode;
}
console.warn(
`Primer Spec: Invalid subtheme mode: ${mode}. Reverting to 'system'`,
);
return 'system';
}