Skip to content

Commit 7d1dfb6

Browse files
Merge pull request scratchfoundation#4974 from ericrosenbaum/feature/sound-editor-selection4
Sound editor update: selection-based editing
2 parents 5b1670f + 6bbff14 commit 7d1dfb6

40 files changed

+1330
-413
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"babel-loader": "^8.0.4",
4545
"base64-loader": "1.0.0",
4646
"bowser": "1.9.4",
47-
"chromedriver": "74.0.0",
47+
"chromedriver": "75.1.0",
4848
"classnames": "2.2.6",
4949
"computed-style-to-inline-style": "3.0.0",
5050
"copy-webpack-plugin": "^4.5.1",

src/components/asset-panel/asset-panel.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
display: flex;
2525
flex-grow: 1;
2626
flex-shrink: 1;
27-
overflow-y: auto;
27+
overflow: visible;
2828
}
2929

3030
[dir="ltr"] .detail-area {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import classNames from 'classnames';
4+
import Box from '../box/box.jsx';
5+
import styles from './audio-trimmer.css';
6+
import SelectionHandle from './selection-handle.jsx';
7+
import Playhead from './playhead.jsx';
8+
9+
const AudioSelector = props => (
10+
<div
11+
className={classNames(styles.absolute, styles.selector)}
12+
ref={props.containerRef}
13+
onMouseDown={props.onNewSelectionMouseDown}
14+
onTouchStart={props.onNewSelectionMouseDown}
15+
>
16+
{props.trimStart === null ? null : (
17+
<Box
18+
className={classNames(styles.absolute)}
19+
style={{
20+
left: `${props.trimStart * 100}%`,
21+
width: `${100 * (props.trimEnd - props.trimStart)}%`
22+
}}
23+
>
24+
<Box className={classNames(styles.absolute, styles.selectionBackground)} />
25+
<SelectionHandle
26+
handleStyle={styles.leftHandle}
27+
onMouseDown={props.onTrimStartMouseDown}
28+
/>
29+
<SelectionHandle
30+
handleStyle={styles.rightHandle}
31+
onMouseDown={props.onTrimEndMouseDown}
32+
/>
33+
</Box>
34+
)}
35+
{props.playhead ? (
36+
<Playhead
37+
playbackPosition={props.playhead}
38+
/>
39+
) : null}
40+
</div>
41+
);
42+
43+
AudioSelector.propTypes = {
44+
containerRef: PropTypes.func,
45+
onNewSelectionMouseDown: PropTypes.func.isRequired,
46+
onTrimEndMouseDown: PropTypes.func.isRequired,
47+
onTrimStartMouseDown: PropTypes.func.isRequired,
48+
playhead: PropTypes.number,
49+
trimEnd: PropTypes.number,
50+
trimStart: PropTypes.number
51+
};
52+
53+
export default AudioSelector;

src/components/audio-trimmer/audio-trimmer.css

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
@import "../../css/colors.css";
22

33
$border-radius: 4px;
4-
$trim-handle-width: 12px;
5-
$trim-handle-height: 14px;
4+
$trim-handle-width: 30px;
5+
$trim-handle-height: 30px;
6+
$trim-handle-border: 3px;
67
$stripe-size: 10px;
7-
$hover-scale: 2;
8+
$hover-scale: 1.25;
89

910
.absolute {
1011
position: absolute;
@@ -17,6 +18,10 @@ $hover-scale: 2;
1718
transform: translateZ(0);
1819
}
1920

21+
.selector {
22+
cursor: pointer;
23+
}
24+
2025
.trim-background {
2126
cursor: pointer;
2227
touch-action: none;
@@ -35,6 +40,11 @@ $hover-scale: 2;
3540
);
3641
}
3742

43+
.selection-background {
44+
background: $motion-primary;
45+
opacity: 0.5;
46+
}
47+
3848
.start-trim-background .trim-background-mask {
3949
border-top-left-radius: $border-radius;
4050
border-bottom-left-radius: $border-radius;
@@ -53,6 +63,10 @@ $hover-scale: 2;
5363
border: 1px solid $red-tertiary;
5464
}
5565

66+
.selector .trim-line {
67+
border: 1px solid $motion-tertiary;
68+
}
69+
5670
.playhead-container {
5771
position: absolute;
5872
top: 0;
@@ -68,31 +82,50 @@ $hover-scale: 2;
6882
so that we can use transform: translateX() using percentages.
6983
*/
7084
width: 100%;
85+
height: 100%;
7186
border-left: 1px solid $motion-primary;
7287
border-top: none;
7388
border-bottom: none;
7489
border-right: none;
7590
}
7691

77-
.start-trim-line {
78-
right: 0;
92+
.right-handle {
93+
transform: scaleX(-1);
7994
}
8095

81-
.end-trim-line {
82-
left: 0;
96+
.selector .left-handle {
97+
left: -1px
98+
}
99+
100+
.selector .right-handle {
101+
right: -1px
102+
}
103+
104+
.trimmer .left-handle {
105+
right: -1px
106+
}
107+
108+
.trimmer .right-handle {
109+
left: -1px
83110
}
84111

85112
.trim-handle {
86113
position: absolute;
87-
left: calc(-$trim-handle-width / 2);
88114
width: $trim-handle-width;
89115
height: $trim-handle-height;
116+
right: 0;
117+
user-select: none;
118+
}
119+
120+
.trimmer .trim-handle {
121+
filter: hue-rotate(150deg);
90122
}
91123

92124
.trim-handle img {
93125
position: absolute;
94126
width: $trim-handle-width;
95127
height: $trim-handle-height;
128+
left: calc($trim-handle-border * 1.5);
96129

97130
/* Make sure image dragging isn't triggered */
98131
user-select: none;
@@ -103,22 +136,13 @@ $hover-scale: 2;
103136
}
104137

105138
.top-trim-handle {
106-
top: -$trim-handle-height;
139+
top: calc(-$trim-handle-height + $trim-handle-border);
107140
}
108141

109142
.bottom-trim-handle {
110-
bottom: -$trim-handle-height;
143+
bottom: calc(-$trim-handle-height + $trim-handle-border);
111144
}
112145

113146
.top-trim-handle img {
114-
transform: rotate(180deg);
115-
}
116-
117-
/* Increase handle size when anywhere on draggable area is hovered */
118-
.trim-background:hover img {
119-
transform: scale($hover-scale);
120-
}
121-
122-
.trim-background:hover .top-trim-handle img {
123-
transform: rotate(180deg) scale($hover-scale);
147+
transform: scaleY(-1);
124148
}

src/components/audio-trimmer/audio-trimmer.jsx

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import React from 'react';
33
import classNames from 'classnames';
44
import Box from '../box/box.jsx';
55
import styles from './audio-trimmer.css';
6-
import handleIcon from './icon--handle.svg';
6+
import SelectionHandle from './selection-handle.jsx';
7+
import Playhead from './playhead.jsx';
78

89
const AudioTrimmer = props => (
910
<div
10-
className={styles.absolute}
11+
className={classNames(styles.absolute, styles.trimmer)}
1112
ref={props.containerRef}
1213
>
1314
{props.trimStart === null ? null : (
@@ -20,28 +21,16 @@ const AudioTrimmer = props => (
2021
onTouchStart={props.onTrimStartMouseDown}
2122
>
2223
<Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
23-
<Box className={classNames(styles.trimLine, styles.startTrimLine)}>
24-
<Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.startTrimHandle)}>
25-
<img src={handleIcon} />
26-
</Box>
27-
<Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.startTrimHandle)}>
28-
<img src={handleIcon} />
29-
</Box>
30-
</Box>
24+
<SelectionHandle
25+
handleStyle={styles.leftHandle}
26+
/>
3127
</Box>
3228
)}
33-
3429
{props.playhead ? (
35-
<div className={styles.playheadContainer}>
36-
<div
37-
className={classNames(styles.trimLine, styles.playhead)}
38-
style={{
39-
transform: `translateX(${100 * props.playhead}%)`
40-
}}
41-
/>
42-
</div>
30+
<Playhead
31+
playbackPosition={props.playhead}
32+
/>
4333
) : null}
44-
4534
{props.trimEnd === null ? null : (
4635
<Box
4736
className={classNames(styles.absolute, styles.trimBackground, styles.endTrimBackground)}
@@ -53,14 +42,9 @@ const AudioTrimmer = props => (
5342
onTouchStart={props.onTrimEndMouseDown}
5443
>
5544
<Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
56-
<Box className={classNames(styles.trimLine, styles.endTrimLine)}>
57-
<Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.endTrimHandle)}>
58-
<img src={handleIcon} />
59-
</Box>
60-
<Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.endTrimHandle)}>
61-
<img src={handleIcon} />
62-
</Box>
63-
</Box>
45+
<SelectionHandle
46+
handleStyle={styles.rightHandle}
47+
/>
6448
</Box>
6549
)}
6650
</div>
Lines changed: 12 additions & 14 deletions
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import classNames from 'classnames';
4+
import styles from './audio-trimmer.css';
5+
6+
const Playhead = props => (
7+
<div className={styles.playheadContainer}>
8+
<div
9+
className={classNames(styles.playhead)}
10+
style={{
11+
transform: `translateX(${100 * props.playbackPosition}%)`
12+
}}
13+
/>
14+
</div>
15+
);
16+
17+
Playhead.propTypes = {
18+
playbackPosition: PropTypes.number
19+
};
20+
21+
export default Playhead;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import classNames from 'classnames';
4+
import Box from '../box/box.jsx';
5+
import styles from './audio-trimmer.css';
6+
import handleIcon from './icon--handle.svg';
7+
8+
const SelectionHandle = props => (
9+
<Box
10+
className={classNames(styles.trimLine, props.handleStyle)}
11+
onMouseDown={props.onMouseDown}
12+
onTouchStart={props.onMouseDown}
13+
>
14+
<Box className={classNames(styles.trimHandle, styles.topTrimHandle)}>
15+
<img src={handleIcon} />
16+
</Box>
17+
<Box className={classNames(styles.trimHandle, styles.bottomTrimHandle)}>
18+
<img src={handleIcon} />
19+
</Box>
20+
</Box>
21+
);
22+
23+
SelectionHandle.propTypes = {
24+
handleStyle: PropTypes.string,
25+
onMouseDown: PropTypes.func
26+
};
27+
28+
export default SelectionHandle;

src/components/icon-button/icon-button.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
font-size: 0.75rem;
99
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
1010
color: $motion-primary;
11+
border-radius: 0.5rem;
1112
}
1213

1314
.container + .container {
@@ -16,4 +17,14 @@
1617

1718
.title {
1819
margin-top: 0.5rem;
20+
text-align: center;
21+
}
22+
23+
.disabled {
24+
opacity: 0.5;
25+
pointer-events: none;
26+
}
27+
28+
.container:active {
29+
background-color: $motion-light-transparent;
1930
}

0 commit comments

Comments
 (0)