pxt-core 7.5.2 → 7.5.5
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/cli.js +61 -46
- package/built/pxt.js +256 -91
- package/built/pxtblockly.js +652 -657
- package/built/pxtblocks.d.ts +1 -0
- package/built/pxtblocks.js +98 -42
- package/built/pxtcompiler.js +45 -4
- package/built/pxtlib.d.ts +7 -2
- package/built/pxtlib.js +77 -39
- package/built/pxtpy.js +73 -2
- package/built/server.js +4 -0
- package/built/target.js +1 -1
- package/built/web/authcode/css/main.1cf9dc37.css +2 -0
- package/built/web/authcode/js/main.03da4c20.js +2 -0
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +2 -2
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtcompiler.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtpy.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/react-common-authcode.css +6200 -0
- package/built/web/react-common-skillmap.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/css/{main.e0620cee.chunk.css → main.73b22966.chunk.css} +1 -1
- package/built/web/skillmap/js/{2.f7cdfd75.chunk.js → 2.3e47a285.chunk.js} +2 -2
- package/built/web/skillmap/js/main.2485091f.chunk.js +1 -0
- package/common-docs/faq.md +1 -1
- package/common-docs/translate.md +2 -2
- package/docfiles/apptracking.html +1 -1
- package/docfiles/tracking.html +1 -1
- package/localtypings/projectheader.d.ts +6 -0
- package/package.json +5 -3
- package/pxtarget.json +1 -1
- package/react-common/components/controls/Button.tsx +4 -1
- package/react-common/components/controls/EditorToggle.tsx +153 -0
- package/react-common/components/controls/FocusList.tsx +120 -0
- package/react-common/components/controls/Input.tsx +4 -4
- package/react-common/components/controls/Link.tsx +36 -0
- package/react-common/components/controls/MenuBar.tsx +5 -95
- package/react-common/components/controls/MenuDropdown.tsx +4 -1
- package/react-common/components/controls/Textarea.tsx +103 -0
- package/react-common/components/share/GifInfo.tsx +63 -0
- package/react-common/components/share/GifRecorder.tsx +97 -0
- package/react-common/components/share/Share.tsx +49 -0
- package/react-common/components/share/ShareInfo.tsx +186 -0
- package/react-common/components/share/SocialButton.tsx +53 -0
- package/react-common/styles/controls/Button.less +4 -0
- package/react-common/styles/controls/EditorToggle.less +271 -0
- package/react-common/styles/controls/Modal.less +7 -5
- package/react-common/styles/controls/Textarea.less +81 -0
- package/react-common/styles/react-common-authcode-core.less +10 -0
- package/react-common/styles/react-common-authcode.less +12 -0
- package/react-common/styles/react-common-variables.less +19 -0
- package/react-common/styles/react-common.less +3 -0
- package/react-common/styles/share/share.less +116 -0
- package/theme/image-editor/imageEditor.less +8 -116
- package/webapp/public/authcode.html +1 -0
- package/webapp/public/blockly/blockly_compressed.js +554 -615
- package/webapp/public/index.html +1 -1
- package/webapp/public/run.html +32 -5
- package/webapp/public/skillmap.html +2 -2
- package/built/web/skillmap/js/main.f6866fc6.chunk.js +0 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../controls/Button";
|
|
3
|
+
|
|
4
|
+
type RecorderState = "default" | "recording" | "rendering";
|
|
5
|
+
|
|
6
|
+
export interface GifRecorderProps {
|
|
7
|
+
onScreenshot?: () => void;
|
|
8
|
+
onRecordStart?: () => void;
|
|
9
|
+
onRecordStop?: () => Promise<void>;
|
|
10
|
+
onGifFrame?: (dataUri: ImageData, delay?: number) => boolean;
|
|
11
|
+
registerSimulatorMsgHandler?: (handler: (msg: any) => void) => void;
|
|
12
|
+
unregisterSimulatorMsgHandler?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const GifRecorder = (props: GifRecorderProps) => {
|
|
16
|
+
const { onScreenshot, onRecordStart, onRecordStop, onGifFrame,
|
|
17
|
+
registerSimulatorMsgHandler, unregisterSimulatorMsgHandler } = props;
|
|
18
|
+
const [ recorderState, _setRecorderState ] = React.useState<RecorderState>("default");
|
|
19
|
+
const recorderStateRef = React.useRef(recorderState);
|
|
20
|
+
const targetTheme = pxt.appTarget.appTheme;
|
|
21
|
+
|
|
22
|
+
const setRecorderState = (state: RecorderState) => {
|
|
23
|
+
_setRecorderState(state);
|
|
24
|
+
recorderStateRef.current = state;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const onGifRecordStop = async () => {
|
|
28
|
+
setRecorderState("rendering");
|
|
29
|
+
await onRecordStop();
|
|
30
|
+
setRecorderState("default");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
34
|
+
const pressed = e.key.toLocaleLowerCase();
|
|
35
|
+
if (targetTheme.simScreenshotKey && pressed === targetTheme.simScreenshotKey.toLocaleLowerCase()) {
|
|
36
|
+
onScreenshot();
|
|
37
|
+
} else if (targetTheme.simGifKey && pressed === targetTheme.simGifKey.toLocaleLowerCase()) {
|
|
38
|
+
if (recorderStateRef.current === "recording") {
|
|
39
|
+
onGifRecordStop();
|
|
40
|
+
} else {
|
|
41
|
+
onGifRecordStart();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const handleSimulatorMsg = (e: any) => {
|
|
47
|
+
if (e.type === "screenshot") {
|
|
48
|
+
if (recorderStateRef.current === "recording") {
|
|
49
|
+
// Adds frame, returns true if we've exceeded the max frame count
|
|
50
|
+
if (onGifFrame(e.data, e.delay)) {
|
|
51
|
+
onGifRecordStop();
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
onScreenshot();
|
|
55
|
+
}
|
|
56
|
+
} else if (e.event === "start") {
|
|
57
|
+
onGifRecordStart();
|
|
58
|
+
} else if (e.event === "stop") {
|
|
59
|
+
onGifRecordStop();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
65
|
+
if (registerSimulatorMsgHandler) registerSimulatorMsgHandler(handleSimulatorMsg);
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
69
|
+
if (unregisterSimulatorMsgHandler) unregisterSimulatorMsgHandler();
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const onGifRecordStart = () => {
|
|
74
|
+
setRecorderState("recording");
|
|
75
|
+
onRecordStart();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const screenshotLabel = lf("Take screenshot ({0})", targetTheme.simScreenshotKey);
|
|
79
|
+
const startRecordingLabel = lf("Record game play ({0})", targetTheme.simGifKey);
|
|
80
|
+
const stopRecordingLabel = lf("Stop recording ({0})", targetTheme.simGifKey) ;
|
|
81
|
+
|
|
82
|
+
return <div className="gif-recorder">
|
|
83
|
+
<div className="gif-recorder-label">{lf("Pick your project thumbnail")}</div>
|
|
84
|
+
<div className="gif-recorder-actions">
|
|
85
|
+
{!!onScreenshot && <Button className="teal inverted"
|
|
86
|
+
title={screenshotLabel}
|
|
87
|
+
label={screenshotLabel}
|
|
88
|
+
leftIcon="fas fa-camera"
|
|
89
|
+
onClick={onScreenshot} />}
|
|
90
|
+
{!!onRecordStart && <Button className="teal inverted"
|
|
91
|
+
title={recorderState === "recording" ? stopRecordingLabel : startRecordingLabel}
|
|
92
|
+
label={recorderState === "recording" ? stopRecordingLabel : startRecordingLabel}
|
|
93
|
+
leftIcon={`fas fa-${recorderState === "recording" ? "square" : "circle"}`}
|
|
94
|
+
onClick={recorderState === "recording" ? onGifRecordStop : onGifRecordStart} />}
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/// <reference path="../types.d.ts" />
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ShareInfo } from "./ShareInfo";
|
|
5
|
+
|
|
6
|
+
export interface ShareData {
|
|
7
|
+
url: string;
|
|
8
|
+
embed: {
|
|
9
|
+
code?: string;
|
|
10
|
+
editor?: string;
|
|
11
|
+
simulator?: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
}
|
|
14
|
+
qr?: string;
|
|
15
|
+
error?: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ShareProps {
|
|
19
|
+
projectName: string;
|
|
20
|
+
screenshotUri?: string;
|
|
21
|
+
|
|
22
|
+
screenshotAsync: () => Promise<string>;
|
|
23
|
+
gifRecordAsync: () => Promise<void>;
|
|
24
|
+
gifRenderAsync: () => Promise<string | void>;
|
|
25
|
+
gifAddFrame: (dataUri: ImageData, delay?: number) => boolean;
|
|
26
|
+
publishAsync: (name: string, screenshotUri?: string) => Promise<ShareData>;
|
|
27
|
+
registerSimulatorMsgHandler?: (handler: (msg: any) => void) => void;
|
|
28
|
+
unregisterSimulatorMsgHandler?: () => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Share = (props: ShareProps) => {
|
|
32
|
+
const { projectName, screenshotUri, screenshotAsync, gifRecordAsync, gifRenderAsync, gifAddFrame,
|
|
33
|
+
publishAsync, registerSimulatorMsgHandler, unregisterSimulatorMsgHandler } = props;
|
|
34
|
+
|
|
35
|
+
return <div className="project-share">
|
|
36
|
+
{(!!screenshotAsync || !!gifRecordAsync) && <div className="project-share-simulator">
|
|
37
|
+
<div id="shareLoanedSimulator" />
|
|
38
|
+
</div>}
|
|
39
|
+
<ShareInfo projectName={projectName}
|
|
40
|
+
screenshotUri={screenshotUri}
|
|
41
|
+
screenshotAsync={screenshotAsync}
|
|
42
|
+
gifRecordAsync={gifRecordAsync}
|
|
43
|
+
gifRenderAsync={gifRenderAsync}
|
|
44
|
+
gifAddFrame={gifAddFrame}
|
|
45
|
+
publishAsync={publishAsync}
|
|
46
|
+
registerSimulatorMsgHandler={registerSimulatorMsgHandler}
|
|
47
|
+
unregisterSimulatorMsgHandler={unregisterSimulatorMsgHandler} />
|
|
48
|
+
</div>
|
|
49
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../controls/Button";
|
|
3
|
+
import { EditorToggle } from "../controls/EditorToggle";
|
|
4
|
+
import { Input } from "../controls/Input";
|
|
5
|
+
import { Textarea } from "../controls/Textarea";
|
|
6
|
+
|
|
7
|
+
import { ShareData } from "./Share";
|
|
8
|
+
import { GifInfo } from "./GifInfo";
|
|
9
|
+
import { SocialButton } from "./SocialButton";
|
|
10
|
+
|
|
11
|
+
export interface ShareInfoProps {
|
|
12
|
+
projectName: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
screenshotUri?: string;
|
|
15
|
+
|
|
16
|
+
screenshotAsync?: () => Promise<string>;
|
|
17
|
+
gifRecordAsync?: () => Promise<void>;
|
|
18
|
+
gifRenderAsync?: () => Promise<string | void>;
|
|
19
|
+
gifAddFrame?: (dataUri: ImageData, delay?: number) => boolean;
|
|
20
|
+
publishAsync: (name: string, screenshotUri?: string) => Promise<ShareData>;
|
|
21
|
+
registerSimulatorMsgHandler?: (handler: (msg: any) => void) => void;
|
|
22
|
+
unregisterSimulatorMsgHandler?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const ShareInfo = (props: ShareInfoProps) => {
|
|
26
|
+
const { projectName, description, screenshotUri, screenshotAsync, gifRecordAsync, gifRenderAsync, gifAddFrame,
|
|
27
|
+
publishAsync, registerSimulatorMsgHandler, unregisterSimulatorMsgHandler } = props;
|
|
28
|
+
const [ name, setName ] = React.useState(projectName);
|
|
29
|
+
const [ thumbnailUri, setThumbnailUri ] = React.useState(screenshotUri);
|
|
30
|
+
const [ shareState, setShareState ] = React.useState<"share" | "gifrecord" | "publish">("share");
|
|
31
|
+
const [ shareData, setShareData ] = React.useState<ShareData>();
|
|
32
|
+
const [ embedState, setEmbedState ] = React.useState<"none" | "code" | "editor" | "simulator">("none");
|
|
33
|
+
const [ showQRCode, setShowQRCode ] = React.useState(false);
|
|
34
|
+
|
|
35
|
+
const showSimulator = !!screenshotAsync || !!gifRecordAsync;
|
|
36
|
+
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
setThumbnailUri(screenshotUri)
|
|
39
|
+
}, [screenshotUri])
|
|
40
|
+
|
|
41
|
+
const exitGifRecord = () => {
|
|
42
|
+
setShareState("share");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const applyGifChange = (uri: string) => {
|
|
46
|
+
setThumbnailUri(uri);
|
|
47
|
+
exitGifRecord();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handlePublishClick = async () => {
|
|
51
|
+
let publishedShareData = await publishAsync(name, thumbnailUri);
|
|
52
|
+
setShareData(publishedShareData);
|
|
53
|
+
if (!publishedShareData?.error) setShareState("publish");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const handleCopyClick = () => {
|
|
57
|
+
navigator.clipboard.writeText(shareData.url);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const handleEmbedClick = () => {
|
|
61
|
+
if (embedState === "none") {
|
|
62
|
+
setShowQRCode(false);
|
|
63
|
+
setEmbedState("code");
|
|
64
|
+
} else {
|
|
65
|
+
setEmbedState("none");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const handleQRCodeClick = () => {
|
|
70
|
+
pxt.tickEvent('share.qrtoggle');
|
|
71
|
+
if (!showQRCode) {
|
|
72
|
+
setEmbedState("none");
|
|
73
|
+
setShowQRCode(true);
|
|
74
|
+
} else {
|
|
75
|
+
setShowQRCode(false);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
const embedOptions = [{
|
|
81
|
+
name: "code",
|
|
82
|
+
label: lf("Code"),
|
|
83
|
+
title: lf("Code"),
|
|
84
|
+
focusable: true,
|
|
85
|
+
onClick: () => setEmbedState("code")
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "editor",
|
|
89
|
+
label: lf("Editor"),
|
|
90
|
+
title: lf("Editor"),
|
|
91
|
+
focusable: true,
|
|
92
|
+
onClick: () => setEmbedState("editor")
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "simulator",
|
|
96
|
+
label: lf("Simulator"),
|
|
97
|
+
title: lf("Simulator"),
|
|
98
|
+
focusable: true,
|
|
99
|
+
onClick: () => setEmbedState("simulator")
|
|
100
|
+
}];
|
|
101
|
+
|
|
102
|
+
return <>
|
|
103
|
+
<div className="project-share-info">
|
|
104
|
+
{(shareState === "share" || shareState === "publish") && <>
|
|
105
|
+
{showSimulator && <h2>{lf("About your project")}</h2>}
|
|
106
|
+
<Input label={lf("Project Name")}
|
|
107
|
+
initialValue={name}
|
|
108
|
+
placeholder={lf("Name your project")}
|
|
109
|
+
readOnly={shareState === "publish"}
|
|
110
|
+
onChange={setName} />
|
|
111
|
+
<Textarea label={lf("Description")}
|
|
112
|
+
initialValue={description}
|
|
113
|
+
placeholder={lf("Tell others about your game")}
|
|
114
|
+
readOnly={shareState === "publish"}
|
|
115
|
+
rows={5} />
|
|
116
|
+
{shareState === "share" && <>
|
|
117
|
+
{showSimulator && <div className="project-share-thumbnail">
|
|
118
|
+
{thumbnailUri
|
|
119
|
+
? <img src={thumbnailUri} />
|
|
120
|
+
: <div className="project-thumbnail-placeholder" />
|
|
121
|
+
}
|
|
122
|
+
<Button title={lf("Update project thumbnail")}
|
|
123
|
+
label={lf("Update project thumbnail")}
|
|
124
|
+
onClick={() => setShareState("gifrecord")} />
|
|
125
|
+
</div>}
|
|
126
|
+
<div>{lf("By publishing you agree that you are allowed to share this game.")}</div>
|
|
127
|
+
{shareData?.error && <div className="project-share-error">
|
|
128
|
+
{(shareData.error.statusCode === 413
|
|
129
|
+
&& pxt.appTarget?.cloud?.cloudProviders?.github)
|
|
130
|
+
? lf("Oops! Your project is too big. You can create a GitHub repository to share it.")
|
|
131
|
+
: lf("Oops! There was an error. Please ensure you are connected to the Internet and try again.")}
|
|
132
|
+
</div>}
|
|
133
|
+
<Button className="primary"
|
|
134
|
+
title={lf("Publish to share")}
|
|
135
|
+
label={lf("Publish to share")}
|
|
136
|
+
onClick={handlePublishClick} />
|
|
137
|
+
</>}
|
|
138
|
+
{shareState === "publish" && <div className="project-share-actions">
|
|
139
|
+
<Button className="teal"
|
|
140
|
+
title={lf("Copy link")}
|
|
141
|
+
label={lf("Copy link")}
|
|
142
|
+
leftIcon="fas fa-link"
|
|
143
|
+
onClick={handleCopyClick} />
|
|
144
|
+
<Button className="share-button"
|
|
145
|
+
title={lf("Show QR code")}
|
|
146
|
+
leftIcon="fas fa-qrcode"
|
|
147
|
+
onClick={handleQRCodeClick} />
|
|
148
|
+
<Button className="share-button"
|
|
149
|
+
title={lf("Show embed code")}
|
|
150
|
+
leftIcon="fas fa-code"
|
|
151
|
+
onClick={handleEmbedClick} />
|
|
152
|
+
<SocialButton className="share-button"
|
|
153
|
+
url={shareData?.url}
|
|
154
|
+
type='facebook'
|
|
155
|
+
heading={lf("Share on Facebook")} />
|
|
156
|
+
<SocialButton className="share-button"
|
|
157
|
+
url={shareData?.url}
|
|
158
|
+
type='twitter'
|
|
159
|
+
heading={lf("Share on Twitter")} />
|
|
160
|
+
</div>}
|
|
161
|
+
{embedState !== "none" && <div className="project-embed">
|
|
162
|
+
<EditorToggle id="project-embed-toggle"
|
|
163
|
+
className="slim tablet-compact"
|
|
164
|
+
items={embedOptions}
|
|
165
|
+
selected={embedOptions.findIndex(i => i.name === embedState)} />
|
|
166
|
+
<Textarea readOnly={true}
|
|
167
|
+
rows={5}
|
|
168
|
+
initialValue={shareData?.embed[embedState]} />
|
|
169
|
+
</div>}
|
|
170
|
+
{showQRCode && <div className="project-qrcode">
|
|
171
|
+
<img src={shareData?.qr} />
|
|
172
|
+
</div>}
|
|
173
|
+
</>}
|
|
174
|
+
{shareState === "gifrecord" && <GifInfo
|
|
175
|
+
initialUri={thumbnailUri}
|
|
176
|
+
onApply={applyGifChange}
|
|
177
|
+
onCancel={exitGifRecord}
|
|
178
|
+
screenshotAsync={screenshotAsync}
|
|
179
|
+
gifRecordAsync={gifRecordAsync}
|
|
180
|
+
gifRenderAsync={gifRenderAsync}
|
|
181
|
+
gifAddFrame={gifAddFrame}
|
|
182
|
+
registerSimulatorMsgHandler={registerSimulatorMsgHandler}
|
|
183
|
+
unregisterSimulatorMsgHandler={unregisterSimulatorMsgHandler} />}
|
|
184
|
+
</div>
|
|
185
|
+
</>
|
|
186
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../controls/Button";
|
|
3
|
+
|
|
4
|
+
interface SocialButtonProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
type?: "facebook" | "twitter" | "discourse";
|
|
8
|
+
heading?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const SocialButton = (props: SocialButtonProps) => {
|
|
12
|
+
const { className, url, type, heading } = props;
|
|
13
|
+
|
|
14
|
+
const handleClick = () => {
|
|
15
|
+
const socialOptions = pxt.appTarget.appTheme.socialOptions;
|
|
16
|
+
let socialUrl = '';
|
|
17
|
+
switch (type) {
|
|
18
|
+
case "facebook": {
|
|
19
|
+
socialUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
case "twitter": {
|
|
23
|
+
let twitterText = lf("Check out what I made!");
|
|
24
|
+
if (socialOptions.twitterHandle && socialOptions.orgTwitterHandle) {
|
|
25
|
+
twitterText = lf("Check out what I made with @{0} and @{1}!", socialOptions.twitterHandle, socialOptions.orgTwitterHandle);
|
|
26
|
+
} else if (socialOptions.twitterHandle) {
|
|
27
|
+
twitterText = lf("Check out what I made with @{0}!", socialOptions.twitterHandle);
|
|
28
|
+
} else if (socialOptions.orgTwitterHandle) {
|
|
29
|
+
twitterText = lf("Check out what I made with @{0}!", socialOptions.orgTwitterHandle);
|
|
30
|
+
}
|
|
31
|
+
socialUrl = `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}` +
|
|
32
|
+
`&text=${encodeURIComponent(twitterText)}` +
|
|
33
|
+
(socialOptions.hashtags ? `&hashtags=${encodeURIComponent(socialOptions.hashtags)}` : '') +
|
|
34
|
+
(socialOptions.related ? `&related=${encodeURIComponent(socialOptions.related)}` : '');
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case "discourse": {
|
|
38
|
+
// https://meta.discourse.org/t/compose-a-new-pre-filled-topic-via-url/28074
|
|
39
|
+
socialUrl = `${socialOptions.discourse || "https://forum.makecode.com/"}new-topic?title=${encodeURIComponent(url)}`;
|
|
40
|
+
if (socialOptions.discourseCategory)
|
|
41
|
+
socialUrl += `&category=${encodeURIComponent(socialOptions.discourseCategory)}`;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
pxt.BrowserUtils.popupWindow(socialUrl, heading, 600, 600);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <Button className={className}
|
|
49
|
+
ariaLabel={type}
|
|
50
|
+
title={type}
|
|
51
|
+
leftIcon={`icon ${type}`}
|
|
52
|
+
onClick={handleClick} />
|
|
53
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
.common-editor-toggle-outer {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.common-editor-toggle {
|
|
6
|
+
position: relative;
|
|
7
|
+
border-radius: 0.2rem;
|
|
8
|
+
border: @editorToggleBorderWidth solid @editorToggleBorderColor;
|
|
9
|
+
background: @editorToggleBackgroundColor;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Used in image/tilemap/animation editor
|
|
15
|
+
.common-editor-toggle.slim .common-editor-toggle-item > .common-button {
|
|
16
|
+
padding: .6rem 2rem .75rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.common-editor-toggle-item {
|
|
20
|
+
z-index: 1;
|
|
21
|
+
flex: 3;
|
|
22
|
+
|
|
23
|
+
.common-menu-dropdown {
|
|
24
|
+
flex: 4;
|
|
25
|
+
|
|
26
|
+
& > .common-button {
|
|
27
|
+
padding: 0;
|
|
28
|
+
width: 100%;
|
|
29
|
+
border-left: 1px solid @editorToggleBorderColor;
|
|
30
|
+
border-right: 1px solid @editorToggleBorderColor;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.common-menu-dropdown-pane {
|
|
34
|
+
border-top: 1px solid @editorToggleBorderColor;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
& > .common-button {
|
|
39
|
+
background: none;
|
|
40
|
+
color: @buttonTextColorInverted;
|
|
41
|
+
transition: color .25s;
|
|
42
|
+
margin: 0;
|
|
43
|
+
width: 100%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.common-button:focus::after {
|
|
47
|
+
outline: none;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown {
|
|
52
|
+
display: grid;
|
|
53
|
+
grid-template-columns: 3fr 1fr;
|
|
54
|
+
flex: 4;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.common-editor-toggle-item.selected {
|
|
58
|
+
.common-menu-dropdown {
|
|
59
|
+
text-align: center;
|
|
60
|
+
|
|
61
|
+
& > .common-button {
|
|
62
|
+
border-right: none;
|
|
63
|
+
color: @buttonTextColor;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
& > .common-button {
|
|
68
|
+
color: @buttonTextColor;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*****************************************************
|
|
73
|
+
* Toggle Handle *
|
|
74
|
+
****************************************************/
|
|
75
|
+
|
|
76
|
+
.common-editor-toggle-handle {
|
|
77
|
+
position: absolute;
|
|
78
|
+
background: white;
|
|
79
|
+
width: 33%;
|
|
80
|
+
height: 100%;
|
|
81
|
+
transition: margin-left .25s, width .25s;
|
|
82
|
+
border-radius: 0.2rem;
|
|
83
|
+
margin-left: -@editorToggleBorderWidth;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.common-editor-toggle {
|
|
87
|
+
/* toggle size, two items, no dropdown */
|
|
88
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 50% }
|
|
89
|
+
|
|
90
|
+
/* toggle positioning, two items, no dropdown */
|
|
91
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 50%; }
|
|
92
|
+
|
|
93
|
+
/* toggle positioning, three items, no dropdown */
|
|
94
|
+
.common-editor-toggle-item:nth-of-type(1).selected ~ .common-editor-toggle-handle { margin-left: 0; }
|
|
95
|
+
.common-editor-toggle-item:nth-of-type(2).selected ~ .common-editor-toggle-handle { margin-left: 33%; }
|
|
96
|
+
.common-editor-toggle-item:nth-of-type(3).selected ~ .common-editor-toggle-handle { margin-left: 67%; }
|
|
97
|
+
|
|
98
|
+
&.has-dropdown {
|
|
99
|
+
/* flex balance, two items, has dropdown (45% to non-dropdown, 55% to dropdown) */
|
|
100
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown,
|
|
101
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) { flex: 55 }
|
|
102
|
+
.common-editor-toggle-item:first-child:nth-last-child(3),
|
|
103
|
+
.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) ~ .common-editor-toggle-item { flex: 45 }
|
|
104
|
+
|
|
105
|
+
/* toggle size, two items, has dropdown */
|
|
106
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 45% }
|
|
107
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle,
|
|
108
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown.selected:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 55% }
|
|
109
|
+
|
|
110
|
+
/* toggle positioning, two items, first item has dropdown */
|
|
111
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) + .selected ~ .common-editor-toggle-handle { margin-left: 55% }
|
|
112
|
+
|
|
113
|
+
/* toggle positioning, two items, second item has dropdown */
|
|
114
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) + .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { margin-left: 45% }
|
|
115
|
+
|
|
116
|
+
/* toggle size, three items, has dropdown */
|
|
117
|
+
.common-editor-toggle-handle { width: 30% }
|
|
118
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { width: 40%; }
|
|
119
|
+
|
|
120
|
+
/* toggle positioning, three items, has dropdown */
|
|
121
|
+
.common-editor-toggle-item:nth-of-type(2).selected ~ .common-editor-toggle-handle { margin-left: 30%; }
|
|
122
|
+
.common-editor-toggle-item:nth-of-type(3).selected ~ .common-editor-toggle-handle { margin-left: 60%; }
|
|
123
|
+
|
|
124
|
+
/* toggle positioning, three items, first item in toggle has dropdown */
|
|
125
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 40%; }
|
|
126
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 70%; }
|
|
127
|
+
|
|
128
|
+
/* toggle positioning, three items, second item in toggle has dropdown */
|
|
129
|
+
.common-editor-toggle-item + .common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 70%; }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*****************************************************
|
|
134
|
+
* Accessibility Menu *
|
|
135
|
+
****************************************************/
|
|
136
|
+
|
|
137
|
+
.common-toggle-accessibility {
|
|
138
|
+
position: absolute;
|
|
139
|
+
z-index: 2;
|
|
140
|
+
width: 0px;
|
|
141
|
+
height: 0px;
|
|
142
|
+
left: 0;
|
|
143
|
+
background: white;
|
|
144
|
+
|
|
145
|
+
.common-button {
|
|
146
|
+
width: 0px;
|
|
147
|
+
height: 0px;
|
|
148
|
+
min-height: 0px;
|
|
149
|
+
|
|
150
|
+
overflow: hidden;
|
|
151
|
+
padding: 0;
|
|
152
|
+
position: absolute;
|
|
153
|
+
top: 0;
|
|
154
|
+
left: 0;
|
|
155
|
+
}
|
|
156
|
+
.common-button:focus {
|
|
157
|
+
width: 100%;
|
|
158
|
+
height: 100%;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.common-toggle-accessibility:focus-within {
|
|
163
|
+
width: 100%;
|
|
164
|
+
height: 100%;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
/*****************************************************
|
|
169
|
+
* Tablet View *
|
|
170
|
+
****************************************************/
|
|
171
|
+
|
|
172
|
+
@media @tabletAndBelow {
|
|
173
|
+
.common-editor-toggle.tablet-compact {
|
|
174
|
+
& > .common-editor-toggle-item > .common-button {
|
|
175
|
+
padding-left: 0.5rem;
|
|
176
|
+
padding-right: 0.5rem;
|
|
177
|
+
.common-button-label {
|
|
178
|
+
display: none;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
&.has-dropdown {
|
|
183
|
+
.common-editor-toggle-item {
|
|
184
|
+
flex: 25;
|
|
185
|
+
}
|
|
186
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown {
|
|
187
|
+
flex: 50;
|
|
188
|
+
display: grid;
|
|
189
|
+
grid-template-columns: 1fr 1fr;
|
|
190
|
+
}
|
|
191
|
+
.common-editor-toggle-handle { width: 25%; }
|
|
192
|
+
.common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { width: 50%; }
|
|
193
|
+
|
|
194
|
+
/* flex balance, two items, has dropdown (33% to non-dropdown, 67% to dropdown) */
|
|
195
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown,
|
|
196
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) { flex: 67 }
|
|
197
|
+
.common-editor-toggle-item:first-child:nth-last-child(3),
|
|
198
|
+
.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) ~ .common-editor-toggle-item { flex: 33 }
|
|
199
|
+
|
|
200
|
+
/* toggle size, two items, has dropdown */
|
|
201
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 33% }
|
|
202
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle,
|
|
203
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown.selected:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 67% }
|
|
204
|
+
|
|
205
|
+
/* toggle positioning, two items, first item has dropdown */
|
|
206
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) + .selected ~ .common-editor-toggle-handle { margin-left: 67% }
|
|
207
|
+
|
|
208
|
+
/* toggle positioning, two items, second item has dropdown */
|
|
209
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) + .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { margin-left: 33% }
|
|
210
|
+
|
|
211
|
+
/* toggle positioning, first item in toggle has dropdown */
|
|
212
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 50%; }
|
|
213
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 75%; }
|
|
214
|
+
|
|
215
|
+
/* toggle positioning, second item in toggle has dropdown */
|
|
216
|
+
.common-editor-toggle-item + .common-editor-toggle-item.common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { margin-left: 25%; }
|
|
217
|
+
.common-editor-toggle-item + .common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 75%; }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Same as tablet
|
|
223
|
+
@media @mobileAndBelow {
|
|
224
|
+
.common-editor-toggle.mobile-compact {
|
|
225
|
+
& > .common-editor-toggle-item > .common-button {
|
|
226
|
+
padding-left: 0.5rem;
|
|
227
|
+
padding-right: 0.5rem;
|
|
228
|
+
.common-button-label {
|
|
229
|
+
display: none;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
&.has-dropdown {
|
|
234
|
+
.common-editor-toggle-item {
|
|
235
|
+
flex: 25;
|
|
236
|
+
}
|
|
237
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown {
|
|
238
|
+
flex: 50;
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: 1fr 1fr;
|
|
241
|
+
}
|
|
242
|
+
.common-editor-toggle-handle { width: 25%; }
|
|
243
|
+
.common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { width: 50%; }
|
|
244
|
+
|
|
245
|
+
/* flex balance, two items, has dropdown (33% to non-dropdown, 67% to dropdown) */
|
|
246
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown,
|
|
247
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) { flex: 67 }
|
|
248
|
+
.common-editor-toggle-item:first-child:nth-last-child(3),
|
|
249
|
+
.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) ~ .common-editor-toggle-item { flex: 33 }
|
|
250
|
+
|
|
251
|
+
/* toggle size, two items, has dropdown */
|
|
252
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 33% }
|
|
253
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) ~ .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle,
|
|
254
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown.selected:first-child:nth-last-child(3) ~ .common-editor-toggle-handle { width: 67% }
|
|
255
|
+
|
|
256
|
+
/* toggle positioning, two items, first item has dropdown */
|
|
257
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown:first-child:nth-last-child(3) + .selected ~ .common-editor-toggle-handle { margin-left: 67% }
|
|
258
|
+
|
|
259
|
+
/* toggle positioning, two items, second item has dropdown */
|
|
260
|
+
.common-editor-toggle-item:first-child:nth-last-child(3) + .common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { margin-left: 33% }
|
|
261
|
+
|
|
262
|
+
/* toggle positioning, first item in toggle has dropdown */
|
|
263
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 50%; }
|
|
264
|
+
.common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 75%; }
|
|
265
|
+
|
|
266
|
+
/* toggle positioning, second item in toggle has dropdown */
|
|
267
|
+
.common-editor-toggle-item + .common-editor-toggle-item.common-editor-toggle-item-dropdown.selected ~ .common-editor-toggle-handle { margin-left: 25%; }
|
|
268
|
+
.common-editor-toggle-item + .common-editor-toggle-item.common-editor-toggle-item-dropdown + .common-editor-toggle-item.selected ~ .common-editor-toggle-handle { margin-left: 75%; }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|