mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Merge pull request #648 from storytold/entity-input-mobile-improvements
EntityInput mobile improvements
This commit is contained in:
5 files changed
+199
-139
No files matched your search
@@ -0,0 +1,99 @@
|
||||
@import "scss/colours.scss";
|
||||
@import "scss/site-refresh.scss";
|
||||
|
||||
.fy-zoom-slider {
|
||||
display: flex;
|
||||
|
||||
&.fy-zoom-slider-vertical {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
.fy-zoom-slider-container {
|
||||
height: 100%;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track {
|
||||
height: 100%;
|
||||
width: 2.25rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track-bar {
|
||||
width: 0.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.fy-zoom-slider-horizontal {
|
||||
width: 100%;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.fy-zoom-slider-container {
|
||||
width: 100%;
|
||||
margin: 0 1.5rem;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track {
|
||||
width: 100%;
|
||||
height: 2.25rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track-bar {
|
||||
height: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
color: rgba(#fff, 0.25);
|
||||
cursor: pointer;
|
||||
transition: color 0.25s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: rgba(#fff, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.fy-zoom-slider-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track {
|
||||
display: inline-flex;
|
||||
|
||||
.fy-zoom-slider-track-bar {
|
||||
background-color: $secondary-darker;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: inset 0 5px 10px rgb(#000, 0.25);
|
||||
border-bottom: 1px solid rgba(#fff, 0.07);
|
||||
}
|
||||
}
|
||||
|
||||
.fy-zoom-slider-thumb {
|
||||
color: rgba(#fff, 0.6);
|
||||
border: 1px solid rgba(#fff, 0.04);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
box-shadow: 0 5px 10px rgb(#000, 0.25);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
background-color: $secondary;
|
||||
border-radius: 0.75rem;
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
|
||||
&:focus-visible {
|
||||
background-color: $secondary-light;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,15 @@ import {
|
||||
faMagnifyingGlassPlus,
|
||||
faMagnifyingGlassMinus,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import "./ZoomSlider.scss";
|
||||
|
||||
export interface ZoomSliderOnChangeEvent {
|
||||
target: { name?: string; type: "zoom"; value: number };
|
||||
}
|
||||
|
||||
interface Props {
|
||||
interface ZoomSliderProps {
|
||||
name?: string;
|
||||
horizontal?: boolean;
|
||||
onChange: (event: ZoomSliderOnChangeEvent) => void;
|
||||
value: number;
|
||||
}
|
||||
@@ -35,37 +37,64 @@ const thumb =
|
||||
|
||||
export default function ZoomSlider({
|
||||
name = "",
|
||||
horizontal,
|
||||
onChange: inChange,
|
||||
value,
|
||||
}: Props) {
|
||||
}: ZoomSliderProps) {
|
||||
const onChange = (newVal: number[]) =>
|
||||
inChange({
|
||||
target: { ...(name && { name }), type: "zoom", value: newVal[0] },
|
||||
});
|
||||
|
||||
// react-easy-crop returns a zoom value from 1 to 3, so a range of 2 when starting with 0
|
||||
|
||||
const valueRange = 2;
|
||||
const incrementDecrement = valueRange / 100;
|
||||
|
||||
return (
|
||||
<div {...{ className: "fy-zoom-slider" }}>
|
||||
<Icon {...{ icon: faMagnifyingGlassPlus }} />
|
||||
<div
|
||||
{...{
|
||||
className: `fy-zoom-slider${
|
||||
horizontal ? " fy-zoom-slider-horizontal" : " fy-zoom-slider-vertical"
|
||||
}`,
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
{...{
|
||||
icon: faMagnifyingGlassPlus,
|
||||
onClick: () => {
|
||||
if (value < 3) {
|
||||
onChange([value + incrementDecrement]);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div {...{ className: "fy-zoom-slider-container" }}>
|
||||
<Range
|
||||
{...{
|
||||
direction: Direction.Up,
|
||||
...(!horizontal ? { direction: Direction.Up } : {}),
|
||||
min: 1,
|
||||
max: 3,
|
||||
onChange,
|
||||
step: 0.01,
|
||||
renderThumb: thumb(
|
||||
Math.round(((value - 1) / 2) * 100).toString() + "%"
|
||||
Math.round(((value - 1) / valueRange) * 100).toString() + "%"
|
||||
),
|
||||
renderTrack,
|
||||
// renderTrack,
|
||||
// step: step,
|
||||
// thumbTip,
|
||||
values: [value],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Icon {...{ icon: faMagnifyingGlassMinus }} />
|
||||
<Icon
|
||||
{...{
|
||||
icon: faMagnifyingGlassMinus,
|
||||
onClick: () => {
|
||||
if (value > 1) {
|
||||
onChange([value - incrementDecrement]);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+42
-121
@@ -17,7 +17,7 @@
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
// background-color: $panel;
|
||||
width: 100%;
|
||||
|
||||
.fy-entity-input-crop-container-hidden {
|
||||
cursor: default;
|
||||
@@ -38,6 +38,33 @@
|
||||
0 0 15px rgb(#000, 0.5);
|
||||
}
|
||||
|
||||
.fy-entity-input-full {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
&.fy-entity-input-narrow {
|
||||
min-height: 400px;
|
||||
// background-color: red;
|
||||
|
||||
.fy-entity-input-full {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr auto;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fy-entity-input-wide {
|
||||
min-height: 300px;
|
||||
// background-color: red;
|
||||
|
||||
.fy-entity-input-full {
|
||||
grid-template-columns: 1fr fit-content(50%);
|
||||
}
|
||||
}
|
||||
|
||||
.fy-entity-input-play-button {
|
||||
@include video-tint;
|
||||
border: none;
|
||||
@@ -61,14 +88,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.fy-entity-input-square {
|
||||
aspect-ratio: 1/1;
|
||||
height: 300px;
|
||||
|
||||
.fy-entity-input-empty-controls {
|
||||
}
|
||||
}
|
||||
|
||||
.fy-entity-input-landscape {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
@@ -77,16 +96,13 @@
|
||||
|
||||
.fy-entity-input-empty,
|
||||
.fy-entity-input-full {
|
||||
// background-color: red;
|
||||
position: relative;
|
||||
// overflow: hidden;
|
||||
justify-content: center;
|
||||
|
||||
.fy-entity-input-icon {
|
||||
position: absolute;
|
||||
height: 80%;
|
||||
opacity: 75%;
|
||||
// background-color: blue;
|
||||
margin: auto;
|
||||
inset: 0;
|
||||
|
||||
@@ -97,8 +113,6 @@
|
||||
|
||||
.fy-entity-input-file-details {
|
||||
padding: 0 0.5rem;
|
||||
// background-color: red;
|
||||
// display: inline-flex;
|
||||
div {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
@@ -121,70 +135,43 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fy-entity-input-full {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr fit-content(50%);
|
||||
// grid-template-rows: 1fr 1fr;
|
||||
// display: flex;
|
||||
|
||||
// video {
|
||||
// width: 100%;
|
||||
// }
|
||||
}
|
||||
|
||||
.fy-entity-input-media-preview {
|
||||
// display: inline-block;
|
||||
position: relative;
|
||||
// width: 66%;
|
||||
// height: 100%;
|
||||
// width: 100%;
|
||||
// aspect-ratio: 1;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.fy-entity-input-preview-controls {
|
||||
// background-color: green;
|
||||
display: grid;
|
||||
// background-color: red;
|
||||
grid-template-rows: 1fr auto;
|
||||
|
||||
background-color: $secondary-dark;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.fy-entity-input-preview-tools {
|
||||
.fy-entity-input-preview-tools-narrow,
|
||||
.fy-entity-input-preview-tools-wide {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
// grid-template-rows: 1fr auto;
|
||||
// grid-template-columns: auto 1fr;
|
||||
gap: 0.5rem;
|
||||
// display: inline-block;
|
||||
// width: 33%;
|
||||
// height: 100%;
|
||||
background-color: $secondary-dark;
|
||||
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: space-between;
|
||||
// position: absolute;
|
||||
// background-color: red;
|
||||
// background-color: rgba(#000, 0.5);
|
||||
// background: linear-gradient(
|
||||
// 0deg,
|
||||
// rgba(0, 0, 0, 0) 0%,
|
||||
// rgba(0, 0, 0, 0.1) 6.25%,
|
||||
// rgba(0, 0, 0, 0.25) 12.5%,
|
||||
// rgba(0, 0, 0, 0.4) 18.75%,
|
||||
// rgba(0, 0, 0, 0.5) 25%
|
||||
// );
|
||||
// padding: 1rem;
|
||||
// backdrop-filter: blur(2px);
|
||||
overflow: hidden;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
// width: 100%;
|
||||
|
||||
.fy-zoom-slider {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fy-entity-input-preview-tools-wide {
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
|
||||
.fy-entity-input-preview-tools-narrow {
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.fy-entity-input-empty-controls {
|
||||
// background-color: red;
|
||||
position: absolute;
|
||||
@@ -230,69 +217,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fy-zoom-slider {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
// padding: 1rem 0;
|
||||
|
||||
svg {
|
||||
color: rgba(#fff, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
.fy-zoom-slider-container {
|
||||
height: 100%;
|
||||
// background-color: red;
|
||||
margin: 1.5rem 0;
|
||||
// width: 3rem;
|
||||
}
|
||||
|
||||
.fy-zoom-slider-track {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 2.25rem;
|
||||
// background-color: green;
|
||||
|
||||
.fy-zoom-slider-track-bar {
|
||||
background-color: $secondary-darker;
|
||||
border-radius: 0.25rem;
|
||||
// margin: 1rem 0;
|
||||
// flex-shrink: 1;
|
||||
box-shadow:
|
||||
// inset 0 0 1px 1px rgba(#000, 0.16),
|
||||
inset 0 5px 10px rgb(#000, 0.25);
|
||||
border-bottom: 1px solid rgba(#fff, 0.07);
|
||||
// border-left: none;
|
||||
// border-right: none;
|
||||
width: 0.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.fy-zoom-slider-thumb {
|
||||
color: rgba(#fff, 0.6);
|
||||
border: 1px solid rgba(#fff, 0.04);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
box-shadow:
|
||||
// 0 0 0 1px rgba(#000, 0.16),
|
||||
0 5px 10px rgb(#000, 0.25);
|
||||
// @include panel;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
background-color: $secondary;
|
||||
border-radius: 0.75rem;
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
|
||||
&:focus-visible {
|
||||
// color: $secondary;
|
||||
background-color: $secondary-light;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { a, useTransition } from "@react-spring/web";
|
||||
import useMeasure from "react-use-measure";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import Iframe from "react-iframe";
|
||||
import { Area, Point } from "react-easy-crop";
|
||||
@@ -32,7 +33,7 @@ export interface CropProps {
|
||||
|
||||
interface EntityInputProps {
|
||||
accept?: AcceptTypes | AcceptTypes[];
|
||||
aspectRatio?: "square" | "landscape" | "portrait";
|
||||
// aspectRatio?: "square" | "landscape" | "portrait";
|
||||
className?: string;
|
||||
cropProps?: CropProps;
|
||||
debug?: string;
|
||||
@@ -47,6 +48,7 @@ interface EntityInputProps {
|
||||
export interface SlideProps {
|
||||
clear: () => void;
|
||||
cropProps?: CropProps;
|
||||
isNarrow: boolean;
|
||||
media?: MediaFile;
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ const MediaBusy = () => {
|
||||
return <Spinner />;
|
||||
};
|
||||
|
||||
const EntityInputFull = ({ clear, cropProps, media }: SlideProps) => {
|
||||
const EntityInputFull = ({ clear, cropProps, isNarrow, media }: SlideProps) => {
|
||||
const bucketConfig = new BucketConfig();
|
||||
const mediaUrl = media?.public_bucket_path
|
||||
? bucketConfig.getGcsUrl(media.public_bucket_path)
|
||||
@@ -94,6 +96,7 @@ const EntityInputFull = ({ clear, cropProps, media }: SlideProps) => {
|
||||
{...{
|
||||
clear,
|
||||
entityType: "image",
|
||||
isNarrow,
|
||||
media,
|
||||
showCrop: !!cropProps,
|
||||
zoomSliderChange,
|
||||
@@ -119,6 +122,7 @@ const EntityInputFull = ({ clear, cropProps, media }: SlideProps) => {
|
||||
{...{
|
||||
clear,
|
||||
entityType: "video",
|
||||
isNarrow,
|
||||
media,
|
||||
showCrop: !!cropProps,
|
||||
zoomSliderChange,
|
||||
@@ -166,7 +170,6 @@ const AnimatedSlide = ({
|
||||
|
||||
export default function EntityInput({
|
||||
accept,
|
||||
aspectRatio = "square",
|
||||
className,
|
||||
cropProps,
|
||||
debug,
|
||||
@@ -194,6 +197,10 @@ export default function EntityInput({
|
||||
mediaToken: mediaToken || value,
|
||||
});
|
||||
const [updated, updatedSet] = useState(false);
|
||||
const [outerRef, { width }] = useMeasure();
|
||||
|
||||
const isNarrow = (width || 0) <= 480;
|
||||
|
||||
const clear = () => {
|
||||
mediaSet(undefined);
|
||||
mediaTokenSet("");
|
||||
@@ -260,9 +267,10 @@ export default function EntityInput({
|
||||
<Label {...{ label }} />
|
||||
<div
|
||||
{...{
|
||||
className: `fy-entity-input panel-inner${
|
||||
aspectRatio ? " fy-entity-input-" + aspectRatio : ""
|
||||
}${className ? " " + className : ""}`,
|
||||
className: `fy-entity-input ${className ? " " + className : ""}${
|
||||
isNarrow ? " fy-entity-input-narrow" : " fy-entity-input-wide"
|
||||
}`,
|
||||
ref: outerRef,
|
||||
}}
|
||||
>
|
||||
{transitions((style: any, i: number, state: any) => {
|
||||
@@ -283,6 +291,7 @@ export default function EntityInput({
|
||||
className: "fy-entity-input-full",
|
||||
clear,
|
||||
cropProps,
|
||||
isNarrow,
|
||||
media,
|
||||
render: EntityInputFull,
|
||||
...sharedProps,
|
||||
|
||||
+4
-2
@@ -5,6 +5,7 @@ import { Button, ZoomSlider, ZoomSliderOnChangeEvent } from "components/common";
|
||||
interface Props {
|
||||
clear: () => void;
|
||||
entityType: "image" | "video";
|
||||
isNarrow: boolean;
|
||||
media?: MediaFile;
|
||||
showCrop: boolean;
|
||||
zoom: number;
|
||||
@@ -14,6 +15,7 @@ interface Props {
|
||||
export default function EntityInputSidePanel({
|
||||
clear,
|
||||
entityType,
|
||||
isNarrow,
|
||||
media,
|
||||
showCrop,
|
||||
zoomSliderChange,
|
||||
@@ -24,9 +26,9 @@ export default function EntityInputSidePanel({
|
||||
}`;
|
||||
return (
|
||||
<div {...{ className: "fy-entity-input-preview-controls" }}>
|
||||
<div {...{ className: "fy-entity-input-preview-tools" }}>
|
||||
<div {...{ className: `fy-entity-input-preview-tools${ isNarrow ? "-narrow" : "-wide" }` }}>
|
||||
{showCrop && (
|
||||
<ZoomSlider {...{ onChange: zoomSliderChange, value: zoom }} />
|
||||
<ZoomSlider {...{ ...isNarrow ? {horizontal: true } : {}, onChange: zoomSliderChange, value: zoom }} />
|
||||
)}
|
||||
|
||||
<div {...{ className: "fy-entity-input-file-details" }}>
|
||||
|
||||
Reference in new issue
Block a user