Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions react-common/components/controls/DropdownModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from "react";
import { classList, ControlProps } from "../util";
import { Button, ButtonViewProps } from "./Button";
import { DropdownProps } from "./Dropdown";
import { FocusList } from "./FocusList";
import { Modal } from "./Modal";

interface DropdownModalProps extends DropdownProps {
onClose: () => void;
}

export const DropdownModal = (props: DropdownModalProps) => {
const {
className,
ariaLabel,
items,
selectedId,
onItemSelected,
onClose
} = props;

const focusableItems = React.useRef<{[k: string]: HTMLButtonElement}>({});

React.useEffect(() => {
if (Object.keys(focusableItems.current).length) {
focusableItems.current[selectedId ?? 0].focus();
}
}, [selectedId]);


return (
<Modal
className={classList("common-dropdown-modal", className)}
title={ariaLabel}
hideTitle={true}
onClose={onClose}
hideDismissButton={true}
>
<FocusList
role="listbox"
className="common-menu-dropdown-pane common-dropdown-shadow"
childTabStopId={selectedId}
useUpAndDownArrowKeys={true}
onClose={onClose}
>
<ul role="presentation">
{ items.map(item =>
<li key={item.id} role="presentation">
<Button
{...item}
buttonRef={ref => focusableItems.current[item.id] = (ref as HTMLButtonElement)}
className={classList("common-dropdown-item", item.className)}
onClick={() => {
onItemSelected(item.id);
onClose();
}}
ariaSelected={item.id === selectedId}
leftIcon={classList("fas fa-check", selectedId !== item.id && "transparent")}
role="option"/>
</li>
)}
</ul>
</FocusList>
</Modal>
)
}
2 changes: 1 addition & 1 deletion react-common/components/controls/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface InputProps extends ControlProps {
inputClassName?: string;
groupClassName?: string;
initialValue?: string;
label?: string;
label?: string | JSX.Element;
title?: string;
placeholder?: string;
icon?: string;
Expand Down
17 changes: 17 additions & 0 deletions react-common/styles/controls/DropdownModal.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.common-dropdown-modal .common-modal {
.common-modal-header {
display: none;
}
.common-modal-body {
background: none;
}

.common-menu-dropdown-pane {
position: relative;
right: unset;
}

.common-dropdown-item .fas.fa-check.transparent {
opacity: 0;
}
}
1 change: 1 addition & 0 deletions react-common/styles/react-common.less
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@import "controls/Checkbox.less";
@import "controls/DraggableGraph.less";
@import "controls/Dropdown.less";
@import "controls/DropdownModal.less";
@import "controls/EditorToggle.less";
@import "controls/Icon.less";
@import "controls/Input.less";
Expand Down
5 changes: 5 additions & 0 deletions react-common/styles/theming/ThemePickerModal.less
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,8 @@
}
}
}

.hc .theme-picker .theme-card.selected {
border-color: @highContrastTextColor;
box-shadow: inset 0 0 0 2px @highContrastTextColor;
}
32 changes: 30 additions & 2 deletions theme/piano-roll/piano-roll.less
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
--note-event-text-color-hover: #ffffff;
--note-event-border-hover: 2px solid #ffffff;

--header-text-color: black;

--white-key-color: white;
--black-key-color: black;
Expand All @@ -28,7 +27,9 @@

--velocity-slider-color: var(--note-event-color);

--header-background: #f0f0f0;
--header-background: var(--pxt-neutral-background1);
--header-text-color: var(--pxt-neutral-foreground1);

--piano-roll-border-color: var(--workspace-grid-line-color);
}

Expand Down Expand Up @@ -64,6 +65,13 @@
.piano-roll .header-container {
height: var(--header-height);
flex-shrink: 0;

.header > .common-dropdown .common-menu-dropdown-pane {
max-height: 15rem;
overflow-y: auto;
overflow-x: hidden;
z-index: 2;
}
}

.piano-roll .header,
Expand Down Expand Up @@ -128,6 +136,10 @@
&>.common-dropdown #snap-select {
min-width: 4.75rem;
}

.common-button.menu-button {
color: var(--header-text-color);
}
}

.scrollbar.horizontal {
Expand Down Expand Up @@ -340,6 +352,16 @@
background-color: var(--playhead-color);
}

.piano-roll .header .velocity-editor-toggle {
overflow-x: hidden;

label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}

.velocity-editor {
position: relative;
display: flex;
Expand Down Expand Up @@ -421,4 +443,10 @@
.music-editor-edit-controls .common-button.green {
display: none;
}
}

@media @tabletAndBelow {
.common-dropdown > .common-button {
min-width: 8rem;
}
}
45 changes: 24 additions & 21 deletions webapp/src/components/musicEditor/EditControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,28 @@ export const EditControls = (props: EditControlsProps) => {

const showAssetName = !!pxt.appTarget?.appTheme.assetEditor;

return <div className="music-editor-edit-controls">
{nameError &&
<div className="music-editor-name-error">
{nameError}
</div>
}
{showAssetName &&
<Input
placeholder={lf("Asset Name")}
initialValue={assetName || editName}
onBlur={handleNameEdit}
onEnterKey={handleNameEdit} />
}
{!hideDoneButton &&
<Button
className="green"
title={lf("Done")}
label={lf("Done")}
onClick={onDoneClicked} />
}
</div>
return (
<div className="music-editor-edit-controls">
{nameError &&
<div className="music-editor-name-error">
{nameError}
</div>
}
{showAssetName &&
<Input
className="mobile-hidden"
placeholder={lf("Asset Name")}
initialValue={assetName || editName}
onBlur={handleNameEdit}
onEnterKey={handleNameEdit} />
}
{!hideDoneButton &&
<Button
className="green"
title={lf("Done")}
label={lf("Done")}
onClick={onDoneClicked} />
}
</div>
);
}
8 changes: 6 additions & 2 deletions webapp/src/components/musicEditor/PlaybackControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ export const PlaybackControls = (props: PlaybackControlsProps) => {
</div>
<Input
id="music-playback-tempo-input music-editor-label"
label={lf("Tempo:")}
label={
<span className="mobile-hidden tablet-hidden">
{lf("Tempo:")}
</span>
}
initialValue={beatsPerMinute.toString()}
onBlur={handleTempoChange}
onEnterKey={handleTempoChange}
Expand Down Expand Up @@ -165,7 +169,7 @@ export const PlaybackControls = (props: PlaybackControlsProps) => {
onClick={onRedoClick} />
</div>
<div className="music-playback-measures">
<div className="music-editor-label">
<div className="music-editor-label mobile-hidden tablet-hidden">
{lf("Measures:")}
</div>
<Button
Expand Down
92 changes: 92 additions & 0 deletions webapp/src/components/pianoRoll/AssetNameModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Input } from "../../../../react-common/components/controls/Input";
import { Modal, ModalAction } from "../../../../react-common/components/controls/Modal";
import { useState } from "react";
import { isNameTaken } from "../../assets";

interface AssetNameModalProps {
assetName: string;
onAssetNameChanged: (newAssetName: string) => void;
onClose: () => void;
}

export const AssetNameModal = (props: AssetNameModalProps) => {
const {
assetName,
onAssetNameChanged,
onClose
} = props;

const [editName, setEditName] = useState(assetName);
const [nameError, setNameError] = useState<string>();

const handleNameEdit = (newValue: string) => {
let errorMessage = null;

const trimmedName = newValue.trim(); // validate using the trimmed name

if (!trimmedName) {
setEditName("");
setNameError(undefined);
return;
}

if (!pxt.validateAssetName(trimmedName)) {
errorMessage = lf("Names may only contain letters, numbers, '-', '_', and space");
}
else if (isNameTaken(trimmedName) && trimmedName !== assetName) {
errorMessage = lf("This name is already used elsewhere in your project");
}

if (errorMessage) {
setNameError(errorMessage);
}
else {
setNameError(undefined);
}
setEditName(trimmedName);
}

const onSaveClick = () => {
if (!nameError) {
onAssetNameChanged(editName);
onClose();
}
}

const actions: ModalAction[] = [
{
label: lf("Cancel"),
onClick: onClose
},
{
label: lf("Save"),
onClick: onSaveClick,
disabled: !!nameError
}
];

return (
<Modal
className="piano-roll-asset-name-modal"
title={lf("Change Asset Name")}
onClose={onClose}
actions={actions}
>
<div className="piano-roll-asset-name-modal-content">
<label htmlFor="asset-name-input">{lf("Asset Name")}</label>
<Input
id="asset-name-input"
initialValue={editName}
onBlur={handleNameEdit}
onEnterKey={handleNameEdit}
onChange={handleNameEdit}
/>
{nameError &&
<div className="piano-roll-asset-name-error">
{nameError}
</div>
}
</div>
</Modal>
)
}
Loading
Loading