From 0cec90c7ca4a96dcb977f886229455592e95fc02 Mon Sep 17 00:00:00 2001 From: Jake Archibald Date: Tue, 6 Nov 2018 13:31:58 +0000 Subject: [PATCH] Main ui (#214) * Class for file drop * OCD * We don't need the invalid state, as we'll accept all types. * Flattening CSS * Fixing zoom input in Firefox * Fixing 'container' scaleTo * two-up closer design match & smaller line * Fixing edge bug --- src/components/App/index.tsx | 2 +- src/components/App/style.scss | 34 ++-- .../Output/custom-els/PinchZoom/index.ts | 6 +- .../Output/custom-els/TwoUp/index.ts | 10 +- .../Output/custom-els/TwoUp/styles.css | 73 ++++--- src/components/Output/index.tsx | 23 ++- src/components/Output/style.scss | 190 ++++++++---------- src/components/compress/style.scss | 2 +- src/init-app.tsx | 1 + src/lib/icons.tsx | 12 +- src/lib/util.scss | 1 + 11 files changed, 182 insertions(+), 172 deletions(-) diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 002c64d0..fd5006f7 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -74,7 +74,7 @@ export default class App extends Component { render({}: Props, { file, Compress }: State) { return (
- + {(!file) ? : (Compress) diff --git a/src/components/App/style.scss b/src/components/App/style.scss index ef252881..3ddd59c8 100644 --- a/src/components/App/style.scss +++ b/src/components/App/style.scss @@ -12,14 +12,14 @@ Note: These styles are temporary. They will be replaced before going live. contain: strict; } -:global { - file-drop { - overflow: hidden; - touch-action: none; - height:100%; - width:100%; +.drop { + overflow: hidden; + touch-action: none; + height: 100%; + width: 100%; - &:after { + &:global { + &::after { content: ''; position: absolute; display: block; @@ -28,28 +28,20 @@ Note: These styles are temporary. They will be replaced before going live. right: 10px; bottom: 10px; border: 2px dashed #fff; + background-color:rgba(88, 116, 88, 0.2); + border-color: rgba(65, 129, 65, 0.5); border-radius: 10px; opacity: 0; transform: scale(0.95); - transition: opacity 300ms ease, transform 300ms cubic-bezier(.6,2,.6,1), background-color 300ms step-end, border-color 300ms step-end; + transition: all 200ms ease-in; + transition-property: transform, opacity; pointer-events: none; } - &.drop-valid:after, - &.drop-invalid:after { + &.drop-valid::after { opacity: 1; transform: scale(1); - transition: opacity 300ms ease, transform 300ms cubic-bezier(.6,2,.6,1); - } - - &.drop-valid:after { - background-color:rgba(88, 116, 88, 0.2); - border-color: rgba(65, 129, 65, 0.5); - } - - &.drop-invalid:after { - background-color:rgba(119, 85, 85, 0.2); - border-color:rgba(129, 63, 63, 0.5); + transition-timing-function: ease-out; } } } diff --git a/src/components/Output/custom-els/PinchZoom/index.ts b/src/components/Output/custom-els/PinchZoom/index.ts index d36ad919..101407b0 100644 --- a/src/components/Output/custom-els/PinchZoom/index.ts +++ b/src/components/Output/custom-els/PinchZoom/index.ts @@ -145,7 +145,7 @@ export default class PinchZoom extends HTMLElement { const relativeToEl = (relativeTo === 'content' ? this._positioningEl : this); // No content element? Fall back to just setting scale - if (!relativeToEl) { + if (!relativeToEl || !this._positioningEl) { this.setTransform({ scale, allowChangeEvent }); return; } @@ -157,6 +157,10 @@ export default class PinchZoom extends HTMLElement { if (relativeTo === 'content') { originX += this.x; originY += this.y; + } else { + const currentRect = this._positioningEl.getBoundingClientRect(); + originX -= currentRect.left; + originY -= currentRect.top; } this._applyChange({ diff --git a/src/components/Output/custom-els/TwoUp/index.ts b/src/components/Output/custom-els/TwoUp/index.ts index 0c76a5d2..d3cd7d3e 100644 --- a/src/components/Output/custom-els/TwoUp/index.ts +++ b/src/components/Output/custom-els/TwoUp/index.ts @@ -68,12 +68,14 @@ export default class TwoUp extends HTMLElement { } connectedCallback() { - this._handle.innerHTML = `
${ - '' - }
`; - this._childrenChange(); + if (!this._everConnected) { + this._handle.innerHTML = `
${ + `${ + '' + }` + }
`; this._resetPosition(); this._everConnected = true; } diff --git a/src/components/Output/custom-els/TwoUp/styles.css b/src/components/Output/custom-els/TwoUp/styles.css index 2eca1559..8111754f 100644 --- a/src/components/Output/custom-els/TwoUp/styles.css +++ b/src/components/Output/custom-els/TwoUp/styles.css @@ -6,29 +6,41 @@ two-up { --track-color: var(--accent-color); --thumb-background: #fff; --thumb-color: var(--accent-color); + --thumb-size: 62px; + --bar-size: 6px; + --bar-touch-size: 30px; } two-up > * { - /* Overlay all children on top of each other, and let - two-up's layout contain all of them. */ + /* Overlay all children on top of each other, and let two-up's layout contain all of them. */ grid-area: 1/1; } -two-up[legacy-clip-compat] > :not(.twoUpHandle) { +two-up[legacy-clip-compat] > :not(.two-up-handle) { + /* Legacy mode uses clip rather than clip-path (Edge doesn't support clip-path), but clip requires + elements to be positioned absolutely */ position: absolute; } -.twoUpHandle { +.two-up-handle { touch-action: none; position: relative; - width: 10px; - background: var(--track-color); + width: var(--bar-touch-size); transform: translateX(var(--split-point)) translateX(-50%); - box-shadow: inset 4px 0 0 rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.4); will-change: transform; cursor: ew-resize; } +.two-up-handle::before { + content: ''; + display: block; + height: 100%; + width: var(--bar-size); + margin: 0 auto; + box-shadow: inset calc(var(--bar-size) / 2) 0 0 rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.4); + background: var(--track-color); +} + .scrubber { display: flex; position: absolute; @@ -36,51 +48,56 @@ two-up[legacy-clip-compat] > :not(.twoUpHandle) { left: 50%; transform-origin: 50% 50%; transform: translate(-50%, -50%); - width: 62px; - height: 56px; + width: var(--thumb-size); + height: calc(var(--thumb-size) * 0.9); background: var(--thumb-background); border: 1px solid rgba(0,0,0,0.2); - border-radius: 5px; + border-radius: calc(var(--thumb-size) * 0.08); box-shadow: 0 1px 4px rgba(0,0,0,0.1); color: var(--thumb-color); -} -.scrubber svg { - flex: 1; - margin: 0 10px; + box-sizing: border-box; + padding: 0 48%; } -two-up[orientation='vertical'] .twoUpHandle { +.scrubber svg { + flex: 1; +} + +two-up[orientation='vertical'] .two-up-handle { width: auto; - height: 7px; + height: var(--bar-touch-size); transform: translateY(var(--split-point)) translateY(-50%); - box-shadow: inset 0 3px 0 rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.4); cursor: ns-resize; } +two-up[orientation='vertical'] .two-up-handle::before { + width: auto; + height: var(--bar-size); + box-shadow: inset 0 calc(var(--bar-size) / 2) 0 rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.4); + margin: calc((var(--bar-touch-size) - var(--bar-size)) / 2) 0 0 0; +} + two-up[orientation='vertical'] .scrubber { - width: 46px; - height: 40px; - font-size: 18px; box-shadow: 1px 0 4px rgba(0,0,0,0.1); transform: translate(-50%, -50%) rotate(-90deg); } -two-up > :nth-child(1):not(.twoUpHandle) { +two-up > :nth-child(1):not(.two-up-handle) { -webkit-clip-path: inset(0 calc(100% - var(--split-point)) 0 0); clip-path: inset(0 calc(100% - var(--split-point)) 0 0); } -two-up > :nth-child(2):not(.twoUpHandle) { +two-up > :nth-child(2):not(.two-up-handle) { -webkit-clip-path: inset(0 0 0 var(--split-point)); clip-path: inset(0 0 0 var(--split-point)); } -two-up[orientation='vertical'] > :nth-child(1):not(.twoUpHandle) { +two-up[orientation='vertical'] > :nth-child(1):not(.two-up-handle) { -webkit-clip-path: inset(0 0 calc(100% - var(--split-point)) 0); clip-path: inset(0 0 calc(100% - var(--split-point)) 0); } -two-up[orientation='vertical'] > :nth-child(2):not(.twoUpHandle) { +two-up[orientation='vertical'] > :nth-child(2):not(.two-up-handle) { -webkit-clip-path: inset(var(--split-point) 0 0 0); clip-path: inset(var(--split-point) 0 0 0); } @@ -90,19 +107,19 @@ two-up[orientation='vertical'] > :nth-child(2):not(.twoUpHandle) { It performs way better in Safari. */ @supports not ((clip-path: inset(0 0 0 0)) or (-webkit-clip-path: inset(0 0 0 0))) { - two-up[legacy-clip-compat] > :nth-child(1):not(.twoUpHandle) { + two-up[legacy-clip-compat] > :nth-child(1):not(.two-up-handle) { clip: rect(auto var(--split-point) auto auto); } - two-up[legacy-clip-compat] > :nth-child(2):not(.twoUpHandle) { + two-up[legacy-clip-compat] > :nth-child(2):not(.two-up-handle) { clip: rect(auto auto auto var(--split-point)); } - two-up[orientation='vertical'][legacy-clip-compat] > :nth-child(1):not(.twoUpHandle) { + two-up[orientation='vertical'][legacy-clip-compat] > :nth-child(1):not(.two-up-handle) { clip: rect(auto auto var(--split-point) auto); } - two-up[orientation='vertical'][legacy-clip-compat] > :nth-child(2):not(.twoUpHandle) { + two-up[orientation='vertical'][legacy-clip-compat] > :nth-child(2):not(.two-up-handle) { clip: rect(var(--split-point) auto auto auto); } } diff --git a/src/components/Output/index.tsx b/src/components/Output/index.tsx index 454e8bc9..2aec617a 100644 --- a/src/components/Output/index.tsx +++ b/src/components/Output/index.tsx @@ -113,14 +113,19 @@ export default class Output extends Component { } @bind - private editScale() { + private onScaleValueFocus() { this.setState({ editingScale: true }, () => { - if (this.scaleInput) this.scaleInput.focus(); + if (this.scaleInput) { + // Firefox unfocuses the input straight away unless I force a style calculation here. I have + // no idea why, but it's late and I'm quite tired. + getComputedStyle(this.scaleInput).transform; + this.scaleInput.focus(); + } }); } @bind - private cancelEditScale() { + private onScaleInputBlur() { this.setState({ editingScale: false }); } @@ -188,6 +193,7 @@ export default class Output extends Component {
{ onWheelCapture={this.onRetargetableEvent} > @@ -213,7 +220,7 @@ export default class Output extends Component { }} /> - + {
-
+
@@ -243,11 +250,11 @@ export default class Output extends Component { class={style.zoom} value={Math.round(scale * 100)} onInput={this.onScaleInputChanged} - onBlur={this.cancelEditScale} + onBlur={this.onScaleInputBlur} /> ) : ( - - {Math.round(scale * 100)} + + {Math.round(scale * 100)} % )} diff --git a/src/components/Output/style.scss b/src/components/Output/style.scss index dacb10bf..dd74b11b 100644 --- a/src/components/Output/style.scss +++ b/src/components/Output/style.scss @@ -2,134 +2,68 @@ Note: These styles are temporary. They will be replaced before going live. */ -%fill { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: hidden; - contain: strict; -} - .output { - @extend %fill; + composes: abs-fill from '../../lib/util.scss'; - &:before { + &::before { content: ''; - @extend %fill; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; background: #000; opacity: 0; transition: opacity 500ms ease; } - &.altBackground:before { - opacity: .6; - } - > two-up { - @extend %fill; - --accent-color: var(--button-fg); - - > pinch-zoom { - @extend %fill; - outline: none; - display: flex; - justify-content: center; - align-items: center; - } + &.alt-background::before { + opacity: 0.6; } } +.two-up { + composes: abs-fill from '../../lib/util.scss'; + --accent-color: var(--button-fg); +} + +.pinch-zoom { + composes: abs-fill from '../../lib/util.scss'; + outline: none; + display: flex; + justify-content: center; + align-items: center; +} + .controls { position: absolute; display: flex; justify-content: center; - left: 220px; - right: 220px; - bottom: 0; + top: 0; + left: 0; + right: 0; padding: 9px; overflow: hidden; flex-wrap: wrap; contain: content; - @media (max-width: 680px) { - top: 0; - bottom: auto; - left: 0; - right: 0; + @media (min-width: 680px) { + top: auto; + left: 220px; + right: 220px; + bottom: 0; } +} - > * { - z-index: 2; - } +.zoom-controls { + display: flex; - .group { - display: flex; - } - - .button, - .zoom { - display: flex; - align-items: center; - flex: 0; - box-sizing: border-box; - height: 48px; - padding: 0 16px; - margin: 4px; - background-color: #fff; - border: 1px solid rgba(0,0,0,0.2); - border-radius: 5px; - line-height: 1; - font-size: 110%; - white-space: nowrap; - - &:focus { - box-shadow: 0 0 0 2px var(--button-fg); - outline: none; - z-index: 1; - } - } - - .button { - text-transform: uppercase; - color: var(--button-fg); - cursor: pointer; - text-indent: 6px; - } - - .button:hover { - background-color: #eee; - } - - .zoom { - flex: 0 0 6em; - color: #625E80; - font: inherit; - cursor: text; - width: 6em; - text-align: center; - justify-content: center; - - &:focus { - box-shadow: inset 0 1px 4px rgba(0,0,0,0.2), 0 0 0 2px var(--button-fg); - } - - strong { - position: relative; - top: 1px; - margin: 0 3px 0 0; - color: #888; - font-weight: normal; - border-bottom: 1px dashed #999; - } - } - - .group > :not(:first-child) { + & :not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 0; } - .group > :not(:last-child) { + & :not(:last-child) { margin-right: 0; border-right-width: 0; border-top-right-radius: 0; @@ -137,6 +71,60 @@ Note: These styles are temporary. They will be replaced before going live. } } +.button, +.zoom { + display: flex; + align-items: center; + box-sizing: border-box; + height: 48px; + padding: 0 16px; + margin: 4px; + background-color: #fff; + border: 1px solid rgba(0,0,0,0.2); + border-radius: 5px; + line-height: 1; + white-space: nowrap; + + &:focus { + box-shadow: 0 0 0 2px var(--button-fg); + outline: none; + z-index: 1; + } +} + +.button { + text-transform: uppercase; + color: var(--button-fg); + cursor: pointer; + text-indent: 6px; + font-size: 110%; + + &:hover { + background-color: #eee; + } +} + +.zoom { + color: #625E80; + cursor: text; + width: 6em; + font: inherit; + text-align: center; + justify-content: center; + + &:focus { + box-shadow: inset 0 1px 4px rgba(0,0,0,0.2), 0 0 0 2px var(--button-fg); + } +} + +.zoom-value { + position: relative; + top: 1px; + margin: 0 3px 0 0; + color: #888; + border-bottom: 1px dashed #999; +} + .output-canvas { flex-shrink: 0; } diff --git a/src/components/compress/style.scss b/src/components/compress/style.scss index 2643534f..29fefe9f 100644 --- a/src/components/compress/style.scss +++ b/src/components/compress/style.scss @@ -4,7 +4,6 @@ .option-pair { display: flex; - justify-content: flex-end; width: 100%; height: 100%; @@ -15,5 +14,6 @@ &.vertical { flex-direction: column; + justify-content: flex-end; } } diff --git a/src/init-app.tsx b/src/init-app.tsx index 2cff1229..f0102af0 100644 --- a/src/init-app.tsx +++ b/src/init-app.tsx @@ -16,6 +16,7 @@ if (process.env.NODE_ENV === 'development') { // When an update to any module is received, re-import the app and trigger a full re-render: module.hot.accept('./components/App', () => { + // The linter doesn't like the capital A in App. It is wrong. // tslint:disable-next-line variable-name import('./components/App').then(({ default: App }) => { root = render(, document.body, root); diff --git a/src/lib/icons.tsx b/src/lib/icons.tsx index 7ada39cc..0562b8d9 100644 --- a/src/lib/icons.tsx +++ b/src/lib/icons.tsx @@ -2,31 +2,29 @@ import { h } from 'preact'; // tslint:disable:max-line-length variable-name -export interface IconProps extends JSX.HTMLAttributes {} - -const Icon = (props: IconProps) => ( +const Icon = (props: JSX.HTMLAttributes) => ( ); -export const DownloadIcon = (props: IconProps) => ( +export const DownloadIcon = (props: JSX.HTMLAttributes) => ( ); -export const ToggleIcon = (props: IconProps) => ( +export const ToggleIcon = (props: JSX.HTMLAttributes) => ( ); -export const AddIcon = (props: IconProps) => ( +export const AddIcon = (props: JSX.HTMLAttributes) => ( ); -export const RemoveIcon = (props: IconProps) => ( +export const RemoveIcon = (props: JSX.HTMLAttributes) => ( diff --git a/src/lib/util.scss b/src/lib/util.scss index f5b3241d..b8c7ba48 100644 --- a/src/lib/util.scss +++ b/src/lib/util.scss @@ -5,6 +5,7 @@ width: 100%; height: 100%; box-sizing: border-box; + contain: strict; } .unbutton {