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.
@@ -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
- preferredRepos?: string[]; // list of company/project(#tag) of packages to show by default in search
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
- slug: string;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pxt-core",
3
- "version": "7.5.37",
3
+ "version": "7.5.40",
4
4
  "description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors",
5
5
  "keywords": [
6
6
  "TypeScript",
@@ -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;