pxt-core 7.5.37 → 7.5.40
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 +9 -1
- package/built/pxt.js +52 -35
- package/built/pxtlib.js +42 -33
- package/built/pxtsim.js +1 -1
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/react-common-authcode.css +140 -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/localtypings/pxtarget.d.ts +5 -7
- package/package.json +1 -1
- package/react-common/components/controls/Card.tsx +48 -0
- package/react-common/components/controls/LazyImage.tsx +82 -0
- package/react-common/components/extensions/ExtensionCard.tsx +70 -0
- package/react-common/styles/controls/Button.less +6 -0
- package/react-common/styles/controls/Card.less +53 -0
- package/react-common/styles/controls/LazyImage.less +29 -0
- package/react-common/styles/extensions/ExtensionCard.less +82 -0
- package/react-common/styles/react-common.less +3 -0
- package/theme/common.less +35 -1
|
@@ -34,14 +34,11 @@ declare namespace pxt {
|
|
|
34
34
|
|
|
35
35
|
interface PackagesConfig {
|
|
36
36
|
approvedOrgs?: string[];
|
|
37
|
-
approvedRepos?: string[]; // list of company/project
|
|
38
37
|
releases?: pxt.Map<string[]>; // per major version list of approved company/project#tag
|
|
39
38
|
bannedOrgs?: string[];
|
|
40
39
|
bannedRepos?: string[];
|
|
41
40
|
allowUnapproved?: boolean;
|
|
42
|
-
|
|
43
|
-
preferredRepoLib?: RepoData[];
|
|
44
|
-
approvedRepoLib?: RepoData[];
|
|
41
|
+
approvedRepoLib?: pxt.Map<RepoData>;
|
|
45
42
|
// format:
|
|
46
43
|
// "acme-corp/pxt-widget": "min:v0.1.2" - auto-upgrade to that version
|
|
47
44
|
// "acme-corp/pxt-widget": "dv:foo,bar" - add "disablesVariant": ["foo", "bar"] to pxt.json
|
|
@@ -53,8 +50,9 @@ declare namespace pxt {
|
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
interface RepoData {
|
|
56
|
-
|
|
57
|
-
tags?: string[]
|
|
53
|
+
preferred?: boolean;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
upgrades?: string[];
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
interface ShareConfig {
|
|
@@ -1192,4 +1190,4 @@ declare namespace pxt.auth {
|
|
|
1192
1190
|
type: "skillmap-completion";
|
|
1193
1191
|
sourceURL: string;
|
|
1194
1192
|
}
|
|
1195
|
-
}
|
|
1193
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { classList, ContainerProps, fireClickOnEnter } from "../util";
|
|
3
|
+
|
|
4
|
+
export interface CardProps extends ContainerProps {
|
|
5
|
+
onClick?: () => void;
|
|
6
|
+
tabIndex?: number;
|
|
7
|
+
ariaLabelledBy?: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
labelClass?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Card = (props: CardProps) => {
|
|
13
|
+
const {
|
|
14
|
+
id,
|
|
15
|
+
className,
|
|
16
|
+
role,
|
|
17
|
+
children,
|
|
18
|
+
ariaDescribedBy,
|
|
19
|
+
ariaLabelledBy,
|
|
20
|
+
ariaHidden,
|
|
21
|
+
ariaLabel,
|
|
22
|
+
onClick,
|
|
23
|
+
label,
|
|
24
|
+
labelClass,
|
|
25
|
+
tabIndex
|
|
26
|
+
} = props;
|
|
27
|
+
|
|
28
|
+
return <div
|
|
29
|
+
id={id}
|
|
30
|
+
className={classList("common-card", className)}
|
|
31
|
+
role={role || (onClick ? "button" : undefined)}
|
|
32
|
+
aria-describedby={ariaDescribedBy}
|
|
33
|
+
aria-labelledby={ariaLabelledBy}
|
|
34
|
+
aria-hidden={ariaHidden}
|
|
35
|
+
aria-label={ariaLabel}
|
|
36
|
+
onClick={onClick}
|
|
37
|
+
tabIndex={tabIndex}
|
|
38
|
+
onKeyDown={fireClickOnEnter}>
|
|
39
|
+
<div className="common-card-body">
|
|
40
|
+
{children}
|
|
41
|
+
</div>
|
|
42
|
+
{label &&
|
|
43
|
+
<label className={classList("common-card-label", labelClass)}>
|
|
44
|
+
{label}
|
|
45
|
+
</label>
|
|
46
|
+
}
|
|
47
|
+
</div>
|
|
48
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ControlProps } from "../util";
|
|
3
|
+
|
|
4
|
+
export interface LazyImageProps extends ControlProps {
|
|
5
|
+
src: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let observer: IntersectionObserver;
|
|
11
|
+
|
|
12
|
+
export const LazyImage = (props: LazyImageProps) => {
|
|
13
|
+
const {
|
|
14
|
+
id,
|
|
15
|
+
className,
|
|
16
|
+
role,
|
|
17
|
+
src,
|
|
18
|
+
alt,
|
|
19
|
+
title,
|
|
20
|
+
ariaLabel,
|
|
21
|
+
ariaHidden,
|
|
22
|
+
ariaDescribedBy,
|
|
23
|
+
} = props;
|
|
24
|
+
|
|
25
|
+
initObserver();
|
|
26
|
+
|
|
27
|
+
let imageRef: HTMLImageElement;
|
|
28
|
+
|
|
29
|
+
const handleImageRef = (ref: HTMLImageElement) => {
|
|
30
|
+
if (!ref) return;
|
|
31
|
+
|
|
32
|
+
if (imageRef) observer.unobserve(imageRef);
|
|
33
|
+
imageRef = ref;
|
|
34
|
+
observer.observe(ref);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
return <div className="common-lazy-image-wrapper">
|
|
40
|
+
<img
|
|
41
|
+
id={id}
|
|
42
|
+
ref={handleImageRef}
|
|
43
|
+
className={className}
|
|
44
|
+
data-src={src}
|
|
45
|
+
alt={alt}
|
|
46
|
+
title={title}
|
|
47
|
+
role={role}
|
|
48
|
+
aria-label={ariaLabel}
|
|
49
|
+
aria-hidden={ariaHidden}
|
|
50
|
+
aria-describedby={ariaDescribedBy}
|
|
51
|
+
/>
|
|
52
|
+
<div className="common-spinner" />
|
|
53
|
+
</div>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function initObserver() {
|
|
57
|
+
if (observer) return;
|
|
58
|
+
|
|
59
|
+
const config = {
|
|
60
|
+
// If the image gets within 50px in the Y axis, start the download.
|
|
61
|
+
rootMargin: '50px 0px',
|
|
62
|
+
threshold: 0.01
|
|
63
|
+
};
|
|
64
|
+
const onIntersection: IntersectionObserverCallback = (entries) => {
|
|
65
|
+
entries.forEach(entry => {
|
|
66
|
+
// Are we in viewport?
|
|
67
|
+
if (entry.intersectionRatio > 0) {
|
|
68
|
+
// Stop watching and load the image
|
|
69
|
+
observer.unobserve(entry.target);
|
|
70
|
+
const url = entry.target.getAttribute("data-src");
|
|
71
|
+
(entry.target as HTMLImageElement).src = url;
|
|
72
|
+
|
|
73
|
+
const image = entry.target as HTMLImageElement;
|
|
74
|
+
image.src = url;
|
|
75
|
+
image.onload = () => {
|
|
76
|
+
image.parentElement.classList.add("loaded");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
observer = new IntersectionObserver(onIntersection, config);
|
|
82
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "../controls/Button";
|
|
3
|
+
import { Card } from "../controls/Card";
|
|
4
|
+
import { LazyImage } from "../controls/LazyImage";
|
|
5
|
+
import { classList } from "../util";
|
|
6
|
+
|
|
7
|
+
export interface ExtensionCardProps<U> {
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
imageUrl?: string;
|
|
11
|
+
learnMoreUrl?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
onClick?: (value: U) => void;
|
|
14
|
+
extension?: U;
|
|
15
|
+
loading?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ExtensionCard = <U,>(props: ExtensionCardProps<U>) => {
|
|
19
|
+
const {
|
|
20
|
+
title,
|
|
21
|
+
description,
|
|
22
|
+
imageUrl,
|
|
23
|
+
learnMoreUrl,
|
|
24
|
+
label,
|
|
25
|
+
onClick,
|
|
26
|
+
extension,
|
|
27
|
+
loading
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
const onCardClick = () => {
|
|
31
|
+
if (onClick) onClick(extension);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const id = pxt.Util.guidGen();
|
|
35
|
+
|
|
36
|
+
return <>
|
|
37
|
+
<Card
|
|
38
|
+
className={classList("common-extension-card", loading && "loading")}
|
|
39
|
+
onClick={onCardClick}
|
|
40
|
+
ariaLabelledBy={id + "-title"}
|
|
41
|
+
ariaDescribedBy={id + "-description"}
|
|
42
|
+
tabIndex={onClick && 0}
|
|
43
|
+
label={label}>
|
|
44
|
+
<div className="common-extension-card-contents">
|
|
45
|
+
{!loading && <>
|
|
46
|
+
<LazyImage src={imageUrl} alt={title} />
|
|
47
|
+
<div className="common-extension-card-title" id={id + "-title"}>
|
|
48
|
+
{title}
|
|
49
|
+
</div>
|
|
50
|
+
<div className="common-extension-card-description">
|
|
51
|
+
<div id={id + "-description"}>
|
|
52
|
+
{description}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
{learnMoreUrl &&
|
|
56
|
+
<Button
|
|
57
|
+
className="link-button"
|
|
58
|
+
label={lf("Learn More")}
|
|
59
|
+
title={lf("Learn More")}
|
|
60
|
+
onClick={() => {}}
|
|
61
|
+
href={learnMoreUrl}
|
|
62
|
+
/>
|
|
63
|
+
}
|
|
64
|
+
</>
|
|
65
|
+
}
|
|
66
|
+
</div>
|
|
67
|
+
<div className="common-spinner"/>
|
|
68
|
+
</Card>
|
|
69
|
+
</>
|
|
70
|
+
}
|
|
@@ -223,6 +223,12 @@
|
|
|
223
223
|
text-decoration: underline;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
.common-button.link-button:focus {
|
|
227
|
+
outline: none;
|
|
228
|
+
border: none;
|
|
229
|
+
text-decoration: underline;
|
|
230
|
+
}
|
|
231
|
+
|
|
226
232
|
/****************************************************
|
|
227
233
|
* Circle Buttons *
|
|
228
234
|
****************************************************/
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.common-card {
|
|
2
|
+
width: 18rem;
|
|
3
|
+
height: 20rem;
|
|
4
|
+
border-radius: 0.5rem;
|
|
5
|
+
border: 1px solid @inputBorderColor;
|
|
6
|
+
transition: border 0.1s ease;
|
|
7
|
+
position: relative;
|
|
8
|
+
|
|
9
|
+
.common-card-body {
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
border-radius: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.common-card[role="button"] {
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
|
|
18
|
+
&:hover {
|
|
19
|
+
border: 1px solid @inputBorderColorFocus;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.common-card-label {
|
|
24
|
+
color: @white;
|
|
25
|
+
background-color: @orange;
|
|
26
|
+
|
|
27
|
+
border-top-left-radius: 0.25rem;
|
|
28
|
+
border-bottom-left-radius: 0.25rem;
|
|
29
|
+
|
|
30
|
+
position: absolute;
|
|
31
|
+
top: 1rem;
|
|
32
|
+
right: -1rem;
|
|
33
|
+
|
|
34
|
+
padding: 0.5rem 0.5rem 0.25rem 0.5rem;
|
|
35
|
+
min-width: 5rem;
|
|
36
|
+
font-size: 16px;
|
|
37
|
+
border-color: darken(@orange, 10%);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.common-card-label::after {
|
|
41
|
+
position: absolute;
|
|
42
|
+
content: "";
|
|
43
|
+
top: 100%;
|
|
44
|
+
left: auto;
|
|
45
|
+
right: 0;
|
|
46
|
+
background-color: transparent;
|
|
47
|
+
border-color: transparent;
|
|
48
|
+
border-style: solid;
|
|
49
|
+
border-width: 1.2em 1.2em 0 0;
|
|
50
|
+
border-top-color: inherit;
|
|
51
|
+
width: 0;
|
|
52
|
+
height: 0;
|
|
53
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.common-lazy-image-wrapper {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
align-items: center;
|
|
5
|
+
|
|
6
|
+
.common-spinner {
|
|
7
|
+
position: absolute;
|
|
8
|
+
width: 60px;
|
|
9
|
+
height: 60px;
|
|
10
|
+
|
|
11
|
+
opacity: 1;
|
|
12
|
+
transition: opacity 0.3s ease;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
img {
|
|
16
|
+
opacity: 0;
|
|
17
|
+
transition: opacity 0.3s ease;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.common-lazy-image-wrapper.loaded {
|
|
22
|
+
.common-spinner {
|
|
23
|
+
opacity: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
img {
|
|
27
|
+
opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
.common-extension-card {
|
|
2
|
+
background-color: @white;
|
|
3
|
+
|
|
4
|
+
.common-card-body {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
height: 100%;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.common-extension-card-title {
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
font-size: 18px;
|
|
15
|
+
padding: 0.5rem 1rem 0.25rem 1rem;
|
|
16
|
+
text-overflow: ellipsis;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.common-extension-card-description {
|
|
22
|
+
flex-grow: 1;
|
|
23
|
+
padding-left: 1rem;
|
|
24
|
+
padding-right: 1rem;
|
|
25
|
+
font-size: 16px;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
|
|
28
|
+
div {
|
|
29
|
+
text-overflow: ellipsis;
|
|
30
|
+
display: -webkit-box;
|
|
31
|
+
-webkit-box-orient: vertical;
|
|
32
|
+
-webkit-line-clamp: 3;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
img {
|
|
38
|
+
width: 100%;
|
|
39
|
+
height: 11rem;
|
|
40
|
+
object-fit: cover;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.common-button.link-button {
|
|
45
|
+
text-align: right;
|
|
46
|
+
padding: 0.5rem 1rem;
|
|
47
|
+
background: @white;
|
|
48
|
+
margin: 0;
|
|
49
|
+
border-radius: 0;
|
|
50
|
+
border-top: solid 1px @inputBorderColor;
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.common-extension-card-contents {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
height: 100%;
|
|
58
|
+
width: 100%;
|
|
59
|
+
|
|
60
|
+
opacity: 1;
|
|
61
|
+
transition: opacity 0.3s ease;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.common-spinner {
|
|
65
|
+
opacity: 0;
|
|
66
|
+
transition: opacity 0.3s ease;
|
|
67
|
+
|
|
68
|
+
position: absolute;
|
|
69
|
+
width: 60px;
|
|
70
|
+
height: 60px;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.common-extension-card.loading {
|
|
75
|
+
.common-extension-card-contents {
|
|
76
|
+
opacity: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.common-spinner {
|
|
80
|
+
opacity: 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
@import "profile/profile.less";
|
|
2
2
|
@import "share/share.less";
|
|
3
|
+
@import "extensions/ExtensionCard.less";
|
|
3
4
|
@import "controls/Button.less";
|
|
5
|
+
@import "controls/Card.less";
|
|
4
6
|
@import "controls/Checkbox.less";
|
|
5
7
|
@import "controls/DraggableGraph.less";
|
|
6
8
|
@import "controls/Dropdown.less";
|
|
7
9
|
@import "controls/EditorToggle.less";
|
|
8
10
|
@import "controls/Icon.less";
|
|
9
11
|
@import "controls/Input.less";
|
|
12
|
+
@import "controls/LazyImage.less";
|
|
10
13
|
@import "controls/MenuDropdown.less";
|
|
11
14
|
@import "controls/Modal.less";
|
|
12
15
|
@import "controls/RadioButtonGroup.less";
|
package/theme/common.less
CHANGED
|
@@ -942,6 +942,14 @@ Field editors
|
|
|
942
942
|
.fullscreen.extensions-browser > .common-modal > .common-modal-header {
|
|
943
943
|
height: @mobileMenuHeight;
|
|
944
944
|
}
|
|
945
|
+
|
|
946
|
+
.extensions-browser {
|
|
947
|
+
.extension-header > .import-button>.common-button {
|
|
948
|
+
.common-button-label {
|
|
949
|
+
display: none;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
945
953
|
}
|
|
946
954
|
|
|
947
955
|
@media @mobileAndBelow {
|
|
@@ -1029,12 +1037,30 @@ Field editors
|
|
|
1029
1037
|
cursor: pointer;
|
|
1030
1038
|
}
|
|
1031
1039
|
|
|
1032
|
-
span {
|
|
1040
|
+
>span {
|
|
1041
|
+
padding-right: .5rem;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.common-button {
|
|
1045
|
+
font-size: 1.2rem;
|
|
1033
1046
|
padding-right: .5rem;
|
|
1047
|
+
margin-right: 0rem;
|
|
1034
1048
|
}
|
|
1035
1049
|
}
|
|
1036
1050
|
}
|
|
1037
1051
|
|
|
1052
|
+
.extension-header {
|
|
1053
|
+
font-family: @segoeUIFont;
|
|
1054
|
+
width: 95%;
|
|
1055
|
+
display: flex;
|
|
1056
|
+
align-items: flex-end;
|
|
1057
|
+
justify-content: space-between;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.import-button>.common-button {
|
|
1061
|
+
padding: .8rem 1rem .95rem;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1038
1064
|
.extension-grid {
|
|
1039
1065
|
display: flex;
|
|
1040
1066
|
flex-wrap: wrap;
|
|
@@ -1142,6 +1168,14 @@ Field editors
|
|
|
1142
1168
|
}
|
|
1143
1169
|
}
|
|
1144
1170
|
}
|
|
1171
|
+
|
|
1172
|
+
.extension-cards {
|
|
1173
|
+
display: grid;
|
|
1174
|
+
grid-template-columns: repeat(auto-fit, 18rem);
|
|
1175
|
+
gap: 1rem;
|
|
1176
|
+
width: 100%;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1145
1179
|
.import-extension-modal {
|
|
1146
1180
|
.common-modal-body {
|
|
1147
1181
|
display: flex;
|