Compare commits
3 Commits
one-pass-p
...
copy-to-si
Author | SHA1 | Date | |
---|---|---|---|
7e91f4e5c5 | |||
854d20af12 | |||
97778c5ba6 |
@ -59,13 +59,11 @@ const encoderOptionsComponentMap = {
|
||||
interface Props {
|
||||
mobileView: boolean;
|
||||
source?: SourceImage;
|
||||
imageIndex: number;
|
||||
encoderState: EncoderState;
|
||||
preprocessorState: PreprocessorState;
|
||||
onEncoderTypeChange(newType: EncoderType): void;
|
||||
onEncoderOptionsChange(newOptions: EncoderOptions): void;
|
||||
onPreprocessorOptionsChange(newOptions: PreprocessorState): void;
|
||||
onCopyToOtherClick(): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -116,16 +114,9 @@ export default class Options extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
onCopyToOtherClick(event: Event) {
|
||||
event.preventDefault();
|
||||
this.props.onCopyToOtherClick();
|
||||
}
|
||||
|
||||
render(
|
||||
{
|
||||
source,
|
||||
imageIndex,
|
||||
encoderState,
|
||||
preprocessorState,
|
||||
onEncoderOptionsChange,
|
||||
@ -205,14 +196,6 @@ export default class Options extends Component<Props, State> {
|
||||
/>
|
||||
: null}
|
||||
</Expander>
|
||||
|
||||
<div class={style.optionsCopy}>
|
||||
<button onClick={this.onCopyToOtherClick} class={style.copyButton}>
|
||||
{imageIndex === 1 && '← '}
|
||||
Copy settings across
|
||||
{imageIndex === 0 && ' →'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -55,18 +55,3 @@ $horizontalPadding: 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.options-copy {
|
||||
display: grid;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
composes: unbutton from '../../lib/util.scss';
|
||||
background: #484848;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ import Processor from '../../codecs/processor';
|
||||
import { VectorResizeOptions, BitmapResizeOptions } from '../../codecs/resize/processor-meta';
|
||||
import './custom-els/MultiPanel';
|
||||
import Results from '../results';
|
||||
import { ExpandIcon } from '../../lib/icons';
|
||||
import { ExpandIcon, CopyAcrossIconProps } from '../../lib/icons';
|
||||
|
||||
export interface SourceImage {
|
||||
file: File | Fileish;
|
||||
@ -136,7 +136,7 @@ async function processSvg(blob: Blob): Promise<HTMLImageElement> {
|
||||
const parser = new DOMParser();
|
||||
const text = await blobToText(blob);
|
||||
const document = parser.parseFromString(text, 'image/svg+xml');
|
||||
const svg = document.documentElement;
|
||||
const svg = document.documentElement!;
|
||||
|
||||
if (svg.hasAttribute('width') && svg.hasAttribute('height')) {
|
||||
return blobToImg(blob);
|
||||
@ -156,6 +156,9 @@ async function processSvg(blob: Blob): Promise<HTMLImageElement> {
|
||||
|
||||
// These are only used in the mobile view
|
||||
const resultTitles = ['Top', 'Bottom'];
|
||||
// These are only used in the desktop view
|
||||
const buttonPositions =
|
||||
['download-left', 'download-right'] as ('download-left' | 'download-right')[];
|
||||
|
||||
export default class Compress extends Component<Props, State> {
|
||||
widthQuery = window.matchMedia('(max-width: 599px)');
|
||||
@ -405,26 +408,30 @@ export default class Compress extends Component<Props, State> {
|
||||
<Options
|
||||
source={source}
|
||||
mobileView={mobileView}
|
||||
imageIndex={index}
|
||||
preprocessorState={image.preprocessorState}
|
||||
encoderState={image.encoderState}
|
||||
onEncoderTypeChange={this.onEncoderTypeChange.bind(this, index)}
|
||||
onEncoderOptionsChange={this.onEncoderOptionsChange.bind(this, index)}
|
||||
onPreprocessorOptionsChange={this.onPreprocessorOptionsChange.bind(this, index)}
|
||||
onCopyToOtherClick={this.onCopyToOtherClick.bind(this, index)}
|
||||
/>
|
||||
));
|
||||
|
||||
const results = images.map((image, i) => (
|
||||
const copyDirections =
|
||||
(mobileView ? ['down', 'up'] : ['right', 'left']) as CopyAcrossIconProps['copyDirection'][];
|
||||
|
||||
const results = images.map((image, index) => (
|
||||
<Results
|
||||
downloadUrl={image.downloadUrl}
|
||||
imageFile={image.file}
|
||||
source={source}
|
||||
loading={loading || image.loading}
|
||||
copyDirection={copyDirections[index]}
|
||||
onCopyToOtherClick={this.onCopyToOtherClick.bind(this, index)}
|
||||
buttonPosition={mobileView ? 'stack-right' : buttonPositions[index]}
|
||||
>
|
||||
{!mobileView ? null : [
|
||||
<ExpandIcon class={style.expandIcon} key="expand-icon"/>,
|
||||
`${resultTitles[i]} (${encoderMap[image.encoderState.type].label})`,
|
||||
`${resultTitles[index]} (${encoderMap[image.encoderState.type].label})`,
|
||||
]}
|
||||
</Results>
|
||||
));
|
||||
|
@ -2,10 +2,10 @@ import { h, Component, ComponentChildren, ComponentChild } from 'preact';
|
||||
|
||||
import * as style from './style.scss';
|
||||
import FileSize from './FileSize';
|
||||
import { DownloadIcon } from '../../lib/icons';
|
||||
import { DownloadIcon, CopyAcrossIcon, CopyAcrossIconProps } from '../../lib/icons';
|
||||
import '../custom-els/LoadingSpinner';
|
||||
import { SourceImage } from '../compress';
|
||||
import { Fileish } from '../../lib/initial-util';
|
||||
import { Fileish, bind } from '../../lib/initial-util';
|
||||
|
||||
interface Props {
|
||||
loading: boolean;
|
||||
@ -13,12 +13,21 @@ interface Props {
|
||||
imageFile?: Fileish;
|
||||
downloadUrl?: string;
|
||||
children: ComponentChildren;
|
||||
copyDirection: CopyAcrossIconProps['copyDirection'];
|
||||
buttonPosition: keyof typeof buttonPositionClass;
|
||||
onCopyToOtherClick(): void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
showLoadingState: boolean;
|
||||
}
|
||||
|
||||
const buttonPositionClass = {
|
||||
'stack-right': style.stackRight,
|
||||
'download-right': style.downloadRight,
|
||||
'download-left': style.downloadLeft,
|
||||
};
|
||||
|
||||
const loadingReactionDelay = 500;
|
||||
|
||||
export default class Results extends Component<Props, State> {
|
||||
@ -43,9 +52,19 @@ export default class Results extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
render({ source, imageFile, downloadUrl, children }: Props, { showLoadingState }: State) {
|
||||
@bind
|
||||
private onCopyToOtherClick(event: Event) {
|
||||
event.preventDefault();
|
||||
this.props.onCopyToOtherClick();
|
||||
}
|
||||
|
||||
render(
|
||||
{ source, imageFile, downloadUrl, children, copyDirection, buttonPosition }: Props,
|
||||
{ showLoadingState }: State,
|
||||
) {
|
||||
|
||||
return (
|
||||
<div class={style.results}>
|
||||
<div class={`${style.results} ${buttonPositionClass[buttonPosition]}`}>
|
||||
<div class={style.resultData}>
|
||||
{(children as ComponentChild[])[0]
|
||||
? <div class={style.resultTitle}>{children}</div>
|
||||
@ -59,6 +78,14 @@ export default class Results extends Component<Props, State> {
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class={style.copyToOther}
|
||||
title="Copy settings to other side"
|
||||
onClick={this.onCopyToOtherClick}
|
||||
>
|
||||
<CopyAcrossIcon class={style.copyIcon} copyDirection={copyDirection} />
|
||||
</button>
|
||||
|
||||
<div class={style.download}>
|
||||
{(downloadUrl && imageFile) && (
|
||||
<a
|
||||
|
@ -16,9 +16,17 @@
|
||||
|
||||
.results {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-columns: [text] 1fr [copy-button] auto [download-button] auto;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
font-size: 1.4rem;
|
||||
font-size: 1rem;
|
||||
|
||||
@media (min-width: 400px) {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
@ -26,13 +34,29 @@
|
||||
}
|
||||
|
||||
.result-data {
|
||||
grid-row: 1;
|
||||
grid-column: text;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.download-right {
|
||||
grid-template-columns: [copy-button] auto [text] 1fr [download-button] auto;
|
||||
}
|
||||
|
||||
.download-left {
|
||||
grid-template-columns: [download-button] auto [text] 1fr [copy-button] auto;
|
||||
}
|
||||
|
||||
.stack-right {
|
||||
& .result-data {
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.result-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -40,7 +64,7 @@
|
||||
}
|
||||
|
||||
.size-delta {
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
@ -56,6 +80,8 @@
|
||||
}
|
||||
|
||||
.download {
|
||||
grid-row: 1;
|
||||
grid-column: download-button;
|
||||
background: #34B9EB;
|
||||
--size: 38px;
|
||||
width: var(--size);
|
||||
@ -77,7 +103,8 @@
|
||||
animation: action-leave 0.2s;
|
||||
}
|
||||
|
||||
.download-icon {
|
||||
.download-icon,
|
||||
.copy-icon {
|
||||
color: #fff;
|
||||
display: block;
|
||||
--size: 24px;
|
||||
@ -93,3 +120,12 @@
|
||||
--size: 22px;
|
||||
grid-area: 1/1;
|
||||
}
|
||||
|
||||
.copy-to-other {
|
||||
grid-row: 1;
|
||||
grid-column: copy-button;
|
||||
composes: unbutton from '../../lib/util.scss';
|
||||
composes: download;
|
||||
|
||||
background: #656565;
|
||||
}
|
||||
|
@ -47,3 +47,28 @@ export const ExpandIcon = (props: JSX.HTMLAttributes) => (
|
||||
<path d="M16.6 8.6L12 13.2 7.4 8.6 6 10l6 6 6-6z"/>
|
||||
</Icon>
|
||||
);
|
||||
|
||||
const copyAcrossRotations = {
|
||||
up: 90, right: 180, down: -90, left: 0,
|
||||
};
|
||||
|
||||
export interface CopyAcrossIconProps extends JSX.HTMLAttributes {
|
||||
copyDirection: keyof typeof copyAcrossRotations;
|
||||
}
|
||||
|
||||
export const CopyAcrossIcon = (props: CopyAcrossIconProps) => {
|
||||
const { copyDirection, ...otherProps } = props;
|
||||
const id = 'point-' + copyDirection;
|
||||
const rotation = copyAcrossRotations[copyDirection];
|
||||
|
||||
return (
|
||||
<Icon {...otherProps}>
|
||||
<defs>
|
||||
<clipPath id={id}>
|
||||
<path d="M-12-12v24h24v-24zM4.5 2h-4v3l-5-5 5-5v3h4z" transform={`translate(12 13) rotate(${rotation})`}/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<path clip-path={`url(#${id})`} d="M19 3h-4.2c-.4-1.2-1.5-2-2.8-2s-2.4.8-2.8 2H5a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm-7 0a1 1 0 0 1 0 2c-.6 0-1-.4-1-1s.4-1 1-1z"/>
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user