pxt-core 13.1.21 → 13.1.22
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/built/react-common/components/theming/SimulatorThemePickerModal.d.ts +1 -0
- package/built/react-common/components/theming/ThemePickerModal.d.ts +1 -0
- package/built/react-common/components/theming/themeReset.d.ts +2 -0
- package/built/react-common/components/theming/useThemeReset.d.ts +6 -0
- package/built/target.js +1 -1
- package/built/targetlight.js +1 -1
- package/built/web/main.js +30 -30
- package/built/web/react-common-authcode.css +1 -1
- package/built/web/react-common-multiplayer.css +1 -1
- package/built/web/react-common-skillmap.css +1 -1
- package/built/web/rtlreact-common-authcode.css +1 -1
- package/built/web/rtlreact-common-multiplayer.css +1 -1
- package/built/web/rtlreact-common-skillmap.css +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/semantic.css +1 -1
- package/built/web/skillmap/js/{main.6352b119.js → main.99d90503.js} +2 -2
- package/package.json +1 -1
- package/react-common/components/theming/SimulatorThemePickerModal.tsx +16 -1
- package/react-common/components/theming/ThemePickerModal.tsx +6 -1
- package/react-common/components/theming/themeReset.ts +16 -0
- package/react-common/components/theming/useThemeReset.tsx +67 -0
- package/react-common/styles/theming/ThemePickerModal.less +74 -9
- package/webapp/public/skillmap.html +1 -1
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import { Input } from "../controls/Input";
|
|
|
4
4
|
import { Modal } from "../controls/Modal";
|
|
5
5
|
import { copySimulatorTheme, getSimulatorThemeForLayout } from "./simulatorThemeDefaults";
|
|
6
6
|
import { ThemePickerToggle } from "./ThemePickerModal";
|
|
7
|
+
import { useThemeReset } from "./useThemeReset";
|
|
7
8
|
|
|
8
9
|
export interface SimulatorThemePickerModalProps {
|
|
9
10
|
presets: pxt.SimulatorThemePreset[];
|
|
@@ -16,6 +17,7 @@ export interface SimulatorThemePickerModalProps {
|
|
|
16
17
|
onUseAccountTheme?: () => void | Promise<void>;
|
|
17
18
|
onThemeChanged?: (preference: pxt.auth.SimulatorThemePreference) => void;
|
|
18
19
|
onSave: (preference: pxt.auth.SimulatorThemePreference) => void | Promise<void>;
|
|
20
|
+
onReset?: () => Promise<void>;
|
|
19
21
|
onClose: () => void;
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -59,8 +61,17 @@ export const SimulatorThemePickerModal = (props: SimulatorThemePickerModalProps)
|
|
|
59
61
|
onUseAccountTheme,
|
|
60
62
|
onThemeChanged,
|
|
61
63
|
onSave,
|
|
64
|
+
onReset,
|
|
62
65
|
onClose,
|
|
63
66
|
} = props;
|
|
67
|
+
const aspectRatio = pxt.appTarget.simulator?.aspectRatio || 1.22;
|
|
68
|
+
const previewStyle: React.CSSProperties & {
|
|
69
|
+
"--simulator-theme-aspect-ratio": number;
|
|
70
|
+
"--simulator-theme-padding-bottom": string;
|
|
71
|
+
} = {
|
|
72
|
+
"--simulator-theme-aspect-ratio": aspectRatio,
|
|
73
|
+
"--simulator-theme-padding-bottom": `${100 / aspectRatio}%`,
|
|
74
|
+
};
|
|
64
75
|
const defaultColorFields = getDefaultColorFields(presets[0].theme);
|
|
65
76
|
const getColorFields = (layoutId: string) => {
|
|
66
77
|
const fields = layouts?.find(layout => layout.id === layoutId)?.colorFields;
|
|
@@ -93,6 +104,7 @@ export const SimulatorThemePickerModal = (props: SimulatorThemePickerModalProps)
|
|
|
93
104
|
const layoutId = theme.layout;
|
|
94
105
|
const layoutIsKnown = layoutId === pxt.auth.DEFAULT_SIMULATOR_LAYOUT || layouts?.some(layout => layout.id === layoutId);
|
|
95
106
|
const colorFields = getColorFields(layoutId);
|
|
107
|
+
const { resetAction, confirmation } = useThemeReset("simulator-theme-picker-modal", onReset);
|
|
96
108
|
|
|
97
109
|
const selectPreset = (id: string) => {
|
|
98
110
|
if (id === ACCOUNT_PRESET_ID && onUseAccountTheme) {
|
|
@@ -133,6 +145,8 @@ export const SimulatorThemePickerModal = (props: SimulatorThemePickerModalProps)
|
|
|
133
145
|
onThemeChanged?.({ presetId: CUSTOM_PRESET_ID, theme: nextTheme });
|
|
134
146
|
};
|
|
135
147
|
|
|
148
|
+
if (confirmation) return confirmation;
|
|
149
|
+
|
|
136
150
|
return <Modal
|
|
137
151
|
id="simulator-theme-picker-modal"
|
|
138
152
|
title={lf("Simulator Theme")}
|
|
@@ -143,6 +157,7 @@ export const SimulatorThemePickerModal = (props: SimulatorThemePickerModalProps)
|
|
|
143
157
|
selected="simulator"
|
|
144
158
|
onModeChanged={onEditorThemeClicked} />}
|
|
145
159
|
actions={[
|
|
160
|
+
...(resetAction ? [resetAction] : []),
|
|
146
161
|
{
|
|
147
162
|
label: lf("Apply"),
|
|
148
163
|
className: "primary",
|
|
@@ -151,7 +166,7 @@ export const SimulatorThemePickerModal = (props: SimulatorThemePickerModalProps)
|
|
|
151
166
|
: onSave({ presetId, theme }),
|
|
152
167
|
},
|
|
153
168
|
]}>
|
|
154
|
-
<div className="simulator-theme-picker">
|
|
169
|
+
<div className="simulator-theme-picker" style={previewStyle}>
|
|
155
170
|
{renderPreview(theme)}
|
|
156
171
|
<div className="simulator-theme-controls">
|
|
157
172
|
<div className="simulator-theme-select-field">
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { Modal } from "../controls/Modal";
|
|
3
3
|
import { EditorToggle } from "../controls/EditorToggle";
|
|
4
4
|
import { ThemeCard } from "./ThemeCard";
|
|
5
|
+
import { useThemeReset } from "./useThemeReset";
|
|
5
6
|
|
|
6
7
|
export type ThemePickerMode = "editor" | "simulator";
|
|
7
8
|
|
|
@@ -45,6 +46,7 @@ export interface ThemePickerModalProps {
|
|
|
45
46
|
selectedThemeId?: string;
|
|
46
47
|
onThemeChanged: (theme: pxt.ColorThemeInfo) => void;
|
|
47
48
|
onSave: (theme: pxt.ColorThemeInfo) => void;
|
|
49
|
+
onReset?: () => Promise<void>;
|
|
48
50
|
onSimulatorThemeClicked?: () => void;
|
|
49
51
|
onClose(): void;
|
|
50
52
|
}
|
|
@@ -53,6 +55,9 @@ export const ThemePickerModal = (props: ThemePickerModalProps) => {
|
|
|
53
55
|
props.selectedThemeId || props.themes[0]?.id
|
|
54
56
|
);
|
|
55
57
|
const selectedTheme = props.themes.find(theme => theme.id === selectedThemeId);
|
|
58
|
+
const { resetAction, confirmation } = useThemeReset("theme-picker-modal", props.onReset);
|
|
59
|
+
|
|
60
|
+
if (confirmation) return confirmation;
|
|
56
61
|
|
|
57
62
|
return (
|
|
58
63
|
<Modal
|
|
@@ -64,7 +69,7 @@ export const ThemePickerModal = (props: ThemePickerModalProps) => {
|
|
|
64
69
|
rightHeader={props.onSimulatorThemeClicked && <ThemePickerToggle
|
|
65
70
|
selected="editor"
|
|
66
71
|
onModeChanged={props.onSimulatorThemeClicked} />}
|
|
67
|
-
actions={[{
|
|
72
|
+
actions={[...(resetAction ? [resetAction] : []), {
|
|
68
73
|
label: lf("Apply"),
|
|
69
74
|
className: "primary",
|
|
70
75
|
disabled: !selectedTheme,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getDefaultSimulatorThemePreference } from "./simulatorThemeDefaults";
|
|
2
|
+
|
|
3
|
+
/** Reset this editor's preferences only. */
|
|
4
|
+
export async function resetEditorThemesAsync(
|
|
5
|
+
target: Pick<pxt.TargetBundle, "appTheme" | "colorThemeMap" | "simulator">,
|
|
6
|
+
saveColorTheme: (theme: pxt.ColorThemeInfo) => Promise<void>,
|
|
7
|
+
clearSimulatorTheme: () => Promise<void>
|
|
8
|
+
): Promise<pxt.auth.SimulatorThemePreference | undefined> {
|
|
9
|
+
const defaultThemeId = target.appTheme?.defaultColorTheme;
|
|
10
|
+
const defaultTheme = defaultThemeId ? target.colorThemeMap?.[defaultThemeId] : undefined;
|
|
11
|
+
if (!defaultTheme) throw new Error("No default editor theme configured");
|
|
12
|
+
|
|
13
|
+
await saveColorTheme(defaultTheme);
|
|
14
|
+
await clearSimulatorTheme();
|
|
15
|
+
return getDefaultSimulatorThemePreference(defaultTheme, target.simulator?.themePresets);
|
|
16
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Modal, ModalAction } from "../controls/Modal";
|
|
3
|
+
|
|
4
|
+
/** Shared confirmation for editor-wide pickers, not project-specific theme settings. */
|
|
5
|
+
export function useThemeReset(pickerId: string, onReset: (() => Promise<void>) | undefined) {
|
|
6
|
+
const [confirming, setConfirming] = React.useState(false);
|
|
7
|
+
const [resetting, setResetting] = React.useState(false);
|
|
8
|
+
const [failed, setFailed] = React.useState(false);
|
|
9
|
+
const inFlight = React.useRef(false);
|
|
10
|
+
const restoreFocus = React.useRef(false);
|
|
11
|
+
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
if (!confirming && restoreFocus.current) {
|
|
14
|
+
document.getElementById(pickerId)?.querySelector<HTMLElement>(".theme-reset-button")?.focus();
|
|
15
|
+
restoreFocus.current = false;
|
|
16
|
+
}
|
|
17
|
+
}, [confirming, pickerId]);
|
|
18
|
+
|
|
19
|
+
const cancel = () => {
|
|
20
|
+
if (inFlight.current) return;
|
|
21
|
+
restoreFocus.current = true;
|
|
22
|
+
setConfirming(false);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const confirm = async () => {
|
|
26
|
+
if (!onReset || inFlight.current) return;
|
|
27
|
+
inFlight.current = true;
|
|
28
|
+
setResetting(true);
|
|
29
|
+
setFailed(false);
|
|
30
|
+
try {
|
|
31
|
+
await onReset();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
pxt.reportException(error);
|
|
34
|
+
setFailed(true);
|
|
35
|
+
} finally {
|
|
36
|
+
inFlight.current = false;
|
|
37
|
+
setResetting(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const resetAction: ModalAction | undefined = onReset ? {
|
|
42
|
+
label: lf("Reset"),
|
|
43
|
+
className: "primary inverted theme-reset-button",
|
|
44
|
+
onClick: () => {
|
|
45
|
+
setFailed(false);
|
|
46
|
+
setConfirming(true);
|
|
47
|
+
},
|
|
48
|
+
} : undefined;
|
|
49
|
+
|
|
50
|
+
const confirmation = confirming && <Modal
|
|
51
|
+
key="theme-reset-confirmation"
|
|
52
|
+
title={lf("Reset all themes?")}
|
|
53
|
+
ariaDescribedBy="theme-reset-description"
|
|
54
|
+
onClose={cancel}
|
|
55
|
+
hideDismissButton={resetting}
|
|
56
|
+
actions={[
|
|
57
|
+
{ label: lf("Cancel"), onClick: cancel, disabled: resetting },
|
|
58
|
+
{ label: resetting ? lf("Resetting...") : lf("Reset"), className: "primary", onClick: confirm, disabled: resetting },
|
|
59
|
+
]}>
|
|
60
|
+
<p id="theme-reset-description">
|
|
61
|
+
{lf("Are you sure you want to reset the editor and simulator themes to their defaults? This will remove your editor-wide theme customizations. Themes set directly on projects will not be changed. This action cannot be undone.")}
|
|
62
|
+
</p>
|
|
63
|
+
{failed && <p role="alert">{lf("Unable to reset themes. Please try again.")}</p>}
|
|
64
|
+
</Modal>;
|
|
65
|
+
|
|
66
|
+
return { resetAction, confirmation };
|
|
67
|
+
}
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
.common-modal-footer {
|
|
20
20
|
grid-template-columns: 1fr auto;
|
|
21
21
|
|
|
22
|
+
.theme-reset-button {
|
|
23
|
+
justify-self: start;
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
button:only-child {
|
|
23
27
|
justify-self: end;
|
|
24
28
|
min-width: 8rem;
|
|
@@ -27,7 +31,7 @@
|
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
.theme-picker-modal>.common-modal,
|
|
30
|
-
.simulator-theme-picker-modal
|
|
34
|
+
.simulator-theme-picker-modal>.common-modal {
|
|
31
35
|
height: 90%;
|
|
32
36
|
max-height: 48rem;
|
|
33
37
|
display: flex;
|
|
@@ -70,30 +74,78 @@
|
|
|
70
74
|
padding-left: 1.5rem;
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
.simulator-theme-picker-modal>.common-modal {
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
|
|
80
|
+
.common-modal-body {
|
|
81
|
+
display: flex;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.common-modal-footer {
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
.simulator-theme-picker {
|
|
91
|
+
flex: 1;
|
|
92
|
+
min-width: 0;
|
|
93
|
+
min-height: 0;
|
|
74
94
|
display: grid;
|
|
75
|
-
grid-template-columns: minmax(
|
|
95
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
96
|
+
grid-template-rows: minmax(0, 1fr);
|
|
76
97
|
gap: 2rem;
|
|
77
|
-
align-items:
|
|
98
|
+
align-items: stretch;
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
.simulator-theme-preview {
|
|
102
|
+
position: relative;
|
|
81
103
|
min-width: 0;
|
|
104
|
+
min-height: 0;
|
|
82
105
|
|
|
83
106
|
.simframe {
|
|
84
107
|
margin: 0;
|
|
85
108
|
}
|
|
86
109
|
|
|
110
|
+
.simframe.ui.embed {
|
|
111
|
+
// Fresh homescreen/skillmap frames have no inline ratio from the simulator driver.
|
|
112
|
+
padding-bottom: var(--simulator-theme-padding-bottom);
|
|
113
|
+
}
|
|
114
|
+
|
|
87
115
|
.simframe > .ui.loader {
|
|
88
116
|
display: none;
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
|
|
120
|
+
@media only screen and (min-width: 50.001rem) {
|
|
121
|
+
.simulator-theme-preview .simframe.ui.embed {
|
|
122
|
+
// Cap the viewport at the target's default ratio, even in a short pane.
|
|
123
|
+
// If width is constrained further, the simulator centers itself within the taller viewport.
|
|
124
|
+
// Override the driver's inline padding ratio only while inside the desktop picker.
|
|
125
|
+
position: absolute;
|
|
126
|
+
inset: 0;
|
|
127
|
+
margin: auto;
|
|
128
|
+
width: auto;
|
|
129
|
+
max-width: 100%;
|
|
130
|
+
height: 100%;
|
|
131
|
+
aspect-ratio: var(--simulator-theme-aspect-ratio);
|
|
132
|
+
padding-bottom: 0 !important;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
92
136
|
.simulator-theme-controls {
|
|
93
137
|
display: flex;
|
|
94
138
|
flex-direction: column;
|
|
95
139
|
gap: 0.75rem;
|
|
96
140
|
min-width: 0;
|
|
141
|
+
min-height: 0;
|
|
142
|
+
overflow-y: auto;
|
|
143
|
+
// Leave room for focus outlines inside the independently scrolling controls.
|
|
144
|
+
padding: 0.25rem;
|
|
145
|
+
scrollbar-gutter: stable;
|
|
146
|
+
> * {
|
|
147
|
+
flex-shrink: 0;
|
|
148
|
+
}
|
|
97
149
|
}
|
|
98
150
|
|
|
99
151
|
.simulator-theme-select-field {
|
|
@@ -128,10 +180,14 @@
|
|
|
128
180
|
|
|
129
181
|
.simulator-theme-color {
|
|
130
182
|
display: grid;
|
|
131
|
-
grid-template-columns: minmax(
|
|
183
|
+
grid-template-columns: minmax(0, 1fr) 3rem minmax(0, 1fr);
|
|
132
184
|
gap: 0.5rem;
|
|
133
185
|
align-items: center;
|
|
134
186
|
|
|
187
|
+
> span {
|
|
188
|
+
overflow-wrap: anywhere;
|
|
189
|
+
}
|
|
190
|
+
|
|
135
191
|
.common-input-wrapper {
|
|
136
192
|
min-width: 0;
|
|
137
193
|
}
|
|
@@ -173,18 +229,27 @@
|
|
|
173
229
|
}
|
|
174
230
|
|
|
175
231
|
.simulator-theme-picker {
|
|
232
|
+
flex: none;
|
|
233
|
+
width: 100%;
|
|
176
234
|
grid-template-columns: 1fr;
|
|
235
|
+
grid-template-rows: auto;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.simulator-theme-picker-modal>.common-modal .common-modal-body {
|
|
239
|
+
display: block;
|
|
240
|
+
overflow-y: auto;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.simulator-theme-controls {
|
|
244
|
+
overflow: visible;
|
|
245
|
+
scrollbar-gutter: auto;
|
|
177
246
|
}
|
|
178
247
|
|
|
179
248
|
.simulator-theme-preview {
|
|
180
|
-
max-width: 28rem;
|
|
181
249
|
width: 100%;
|
|
250
|
+
max-width: 28rem;
|
|
182
251
|
justify-self: center;
|
|
183
252
|
}
|
|
184
|
-
|
|
185
|
-
.simulator-theme-color {
|
|
186
|
-
grid-template-columns: minmax(5.5rem, 1fr) 3rem minmax(6.5rem, 1fr);
|
|
187
|
-
}
|
|
188
253
|
}
|
|
189
254
|
|
|
190
255
|
.theme-picker {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="stylesheet" data-rtl="/blb/rtlsemantic.css" href="/blb/semantic.css">
|
|
8
8
|
<link rel="stylesheet" href="/blb/icons.css">
|
|
9
9
|
<link rel="stylesheet" href="/blb/react-common-skillmap.css">
|
|
10
|
-
<script defer="defer" src="/blb/skillmap/js/main.
|
|
10
|
+
<script defer="defer" src="/blb/skillmap/js/main.99d90503.js"></script><link href="/blb/skillmap/css/main.450cb46e.css" rel="stylesheet"></head>
|
|
11
11
|
<body class="pxt-theme-root">
|
|
12
12
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
13
13
|
|