mirror of
https://github.com/filecoin-project/slate.git
synced 2024-09-11 22:07:08 +03:00
Merge pull request #1063 from filecoin-project/@aminejv/fixes
@aminejv/fixes
This commit is contained in:
commit
42fe6545d4
@ -9,6 +9,7 @@ export const sizes = {
|
||||
intercomWidget: 60,
|
||||
sidebar: 416,
|
||||
filterNavbar: 42,
|
||||
carouselMobileFooterHeight: 52,
|
||||
topOffset: 0, //NOTE(martina): Pushes UI down. 16 when there is a persistent announcement banner, 0 otherwise
|
||||
|
||||
mobile: 768,
|
||||
@ -149,8 +150,8 @@ export const zindex = {
|
||||
sidebar: 5,
|
||||
modal: 6,
|
||||
jumper: 7,
|
||||
tooltip: 8,
|
||||
cta: 9,
|
||||
tooltip: 9,
|
||||
cta: 11,
|
||||
};
|
||||
|
||||
export const font = {
|
||||
|
@ -51,6 +51,9 @@ export const useForm = ({
|
||||
if (event) event(e);
|
||||
});
|
||||
|
||||
const setFormValues = ({ name, value }) =>
|
||||
setState((prev) => ({ ...prev, values: { ...prev.values, [name]: value } }));
|
||||
|
||||
/** ---------- NOTE(amine): Input Handlers ---------- */
|
||||
const createOnChangeHandler = (type) => (e) => {
|
||||
const prevValue = state.values[e.target.name];
|
||||
@ -165,6 +168,7 @@ export const useForm = ({
|
||||
getFieldProps,
|
||||
getFormProps,
|
||||
submitForm: submitAsync,
|
||||
setFormValues,
|
||||
values: state.values,
|
||||
isSubmitting: internal.isSubmitting,
|
||||
isValidating: internal.isValidating,
|
||||
@ -527,3 +531,9 @@ export const usePrevious = (value) => {
|
||||
|
||||
return ref.current;
|
||||
};
|
||||
|
||||
export const useToggle = () => {
|
||||
const [isVisible, setVisibility] = React.useState(false);
|
||||
const toggleVisibility = () => setVisibility((prev) => !prev);
|
||||
return [isVisible, toggleVisibility];
|
||||
};
|
||||
|
@ -2269,7 +2269,7 @@ export const FileText = (props) => (
|
||||
/>
|
||||
<path
|
||||
d="M9.333 1.333v4h4M10.667 8.667H5.333M10.667 11.333H5.333M6.667 6H5.333"
|
||||
stroke="#00050A"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.25}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
@ -2398,7 +2398,7 @@ export const FilePlus = (props) => (
|
||||
/>
|
||||
<path
|
||||
d="M9.333 1.333v4h4M8 12V8M6 10h4"
|
||||
stroke="#00050A"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.25}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
@ -94,10 +94,11 @@ const STYLES_HEADER = (theme) => css`
|
||||
top: 0;
|
||||
`;
|
||||
|
||||
const STYLES_UPLOAD_BUTTON = css`
|
||||
const STYLES_UPLOAD_BUTTON = (theme) => css`
|
||||
position: relative;
|
||||
${Styles.CONTAINER_CENTERED};
|
||||
background-color: ${Constants.semantic.bgGrayLight};
|
||||
color: ${theme.semantic.textBlack};
|
||||
border-radius: 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@ -159,7 +160,7 @@ export default function ApplicationHeader({ viewer, page, data, onAction, isMobi
|
||||
) : (
|
||||
<UploadOnboarding onAction={onAction} viewer={viewer} isMobile={isMobile}>
|
||||
<Upload.Provider page={page} data={data} viewer={viewer}>
|
||||
<Upload.Root data={data} isMobile={isMobile}>
|
||||
<Upload.Root page={page} data={data} isMobile={isMobile}>
|
||||
<div css={STYLES_RIGHT}>
|
||||
<UserActions viewer={viewer} />
|
||||
</div>
|
||||
|
@ -8,11 +8,10 @@ import * as SVG from "~/common/svg";
|
||||
import * as Styles from "~/common/styles";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { useField, useForm } from "~/common/hooks";
|
||||
import { Toggle, SignUpPopover, ArrowButton } from "~/components/core/Auth/components";
|
||||
import { useField, useForm, useIsomorphicLayoutEffect } from "~/common/hooks";
|
||||
import { Toggle, SignUpPopover, ArrowButton, AuthField } from "~/components/core/Auth/components";
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
const STYLES_INITIAL_CONTAINER = css`
|
||||
display: flex;
|
||||
@ -41,6 +40,34 @@ const STYLES_LINK_ITEM = (theme) => css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE = (theme) => css`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-top: 16px;
|
||||
border: 1px solid ${theme.system.white};
|
||||
background-color: white;
|
||||
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
|
||||
background-color: ${theme.semantic.bgBlurWhiteTRN};
|
||||
backdrop-filter: blur(75px);
|
||||
}
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
& > * + * {
|
||||
margin-left: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE_PARAGRAPH = (theme) => css`
|
||||
font-size: ${theme.typescale.lvlN1};
|
||||
color: ${theme.system.blue};
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE_BUTTON = (theme) => css`
|
||||
${Styles.BUTTON_RESET};
|
||||
color: ${theme.semantic.textGray};
|
||||
`;
|
||||
|
||||
// NOTE(amine): used to remove content jumping
|
||||
// when switching between signin/signup in mobile
|
||||
const STYLES_SPACER = css`
|
||||
@ -79,12 +106,18 @@ function Initial(
|
||||
onAction,
|
||||
});
|
||||
|
||||
const [signinMessage, setSigninMessage] = React.useState(null);
|
||||
const showSigninMessage = (message) => setSigninMessage(message);
|
||||
const hideSigninMessage = () => setSigninMessage(null);
|
||||
|
||||
// NOTE(amine): Signup view form
|
||||
const {
|
||||
getFieldProps: getSignupFielProps,
|
||||
getFormProps: getSigninFormProps,
|
||||
isSubmitting: isCheckingSignupEmail,
|
||||
setFormValues: setSignupFormValues,
|
||||
submitForm: submitSignupForm,
|
||||
isSubmitting: isCheckingSignupEmail,
|
||||
values: signupFormValues,
|
||||
} = useForm({
|
||||
validateOnBlur: false,
|
||||
initialValues: { email: initialEmail || "" },
|
||||
@ -118,9 +151,36 @@ function Initial(
|
||||
});
|
||||
|
||||
// NOTE(amine): Signin view form
|
||||
const { getFieldProps: getSigninFieldProps, submitField: submitSigninField } = useField({
|
||||
const {
|
||||
getFieldProps: getSigninFieldProps,
|
||||
setFieldValue: setSigninFieldValue,
|
||||
submitField: submitSigninField,
|
||||
value: signinFormValue,
|
||||
isSubmitting: isCheckingSigninEmail,
|
||||
} = useField({
|
||||
validateOnBlur: false,
|
||||
onSubmit: async (emailOrUsername) => goToSigninScene({ emailOrUsername }),
|
||||
onSubmit: async (emailOrUsername) => {
|
||||
let response;
|
||||
if (Validations.email(emailOrUsername)) {
|
||||
response = await Actions.checkEmail({ email: emailOrUsername });
|
||||
} else {
|
||||
response = await Actions.checkUsername({
|
||||
username: emailOrUsername,
|
||||
});
|
||||
}
|
||||
if (response?.data) {
|
||||
goToSigninScene({ emailOrUsername });
|
||||
return;
|
||||
}
|
||||
const message = (
|
||||
<>
|
||||
There isn't an account associated with this{" "}
|
||||
{Validations.email(emailOrUsername) ? "email" : "username"}, <br /> please choose sign up
|
||||
instead.
|
||||
</>
|
||||
);
|
||||
showSigninMessage(message);
|
||||
},
|
||||
validate: (emailOrUsername) => {
|
||||
if (Strings.isEmpty(emailOrUsername)) return "Please enter a username or email";
|
||||
if (!Validations.username(emailOrUsername) && !Validations.email(emailOrUsername))
|
||||
@ -129,6 +189,16 @@ function Initial(
|
||||
initialValue: initialEmail,
|
||||
});
|
||||
|
||||
// NOTE(amine): sync signup and signin values
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (toggleValue === "signup") {
|
||||
setSignupFormValues({ name: "email", value: signinFormValue });
|
||||
} else {
|
||||
setSigninFieldValue(signupFormValues.email);
|
||||
}
|
||||
hideSigninMessage();
|
||||
}, [toggleValue]);
|
||||
|
||||
// NOTE(amine): allow other components to submit this form via a ref
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
submitSignupForm,
|
||||
@ -168,9 +238,21 @@ function Initial(
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</div>
|
||||
{signinMessage && (
|
||||
<div css={STYLES_MESSAGE}>
|
||||
<System.P1 css={STYLES_MESSAGE_PARAGRAPH}>{signinMessage}</System.P1>
|
||||
<button
|
||||
css={STYLES_MESSAGE_BUTTON}
|
||||
style={{ marginTop: 4 }}
|
||||
onClick={hideSigninMessage}
|
||||
>
|
||||
<SVG.Dismiss height={16} width={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{toggleValue === "signup" ? (
|
||||
<form {...getSigninFormProps()}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
label="Sign up with email"
|
||||
placeholder="Email"
|
||||
@ -199,18 +281,34 @@ function Initial(
|
||||
</AnimateSharedLayout>
|
||||
</form>
|
||||
) : (
|
||||
<Field
|
||||
autoFocus
|
||||
label="Email address or username"
|
||||
placeholder="Email/username"
|
||||
icon={ArrowButton}
|
||||
name="email/username"
|
||||
type="text"
|
||||
full
|
||||
{...getSigninFieldProps()}
|
||||
// NOTE(amine): the input component internally is using 16px margin top
|
||||
containerStyle={{ marginTop: "4px" }}
|
||||
/>
|
||||
<AnimateSharedLayout>
|
||||
<motion.div layoutId="auth_signin_field">
|
||||
<AuthField
|
||||
autoFocus
|
||||
label="Email address or username"
|
||||
placeholder="Email/username"
|
||||
icon={
|
||||
isCheckingSigninEmail
|
||||
? ({ style, ...props }) => (
|
||||
<div
|
||||
style={{ width: 20, height: 20, ...style }}
|
||||
css={Styles.CONTAINER_CENTERED}
|
||||
{...props}
|
||||
>
|
||||
<LoaderSpinner height="16px" />
|
||||
</div>
|
||||
)
|
||||
: ArrowButton
|
||||
}
|
||||
name="email/username"
|
||||
type="text"
|
||||
full
|
||||
{...getSigninFieldProps()}
|
||||
// NOTE(amine): the input component internally is using 16px margin top
|
||||
containerStyle={{ marginTop: "4px" }}
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimateSharedLayout>
|
||||
)}
|
||||
|
||||
{showTermsAndServices && (
|
||||
|
@ -5,14 +5,12 @@ import * as Actions from "~/common/actions";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
import * as SVG from "~/common/svg";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { P1 } from "~/components/system";
|
||||
import { useForm } from "~/common/hooks";
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
import { SignUpPopover, Verification } from "~/components/core/Auth/components";
|
||||
import { SignUpPopover, Verification, AuthField } from "~/components/core/Auth/components";
|
||||
|
||||
const STYLES_BACK_BUTTON = css`
|
||||
background: none;
|
||||
@ -110,7 +108,7 @@ export default function ResetPassword({
|
||||
return (
|
||||
<SignUpPopover title={<>Enter new password</>}>
|
||||
<form {...getNewPasswordFormProps()} style={{ marginTop: 72 }}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
placeholder="New password"
|
||||
@ -153,7 +151,7 @@ export default function ResetPassword({
|
||||
}
|
||||
>
|
||||
<form {...getFormProps()} style={{ marginTop: 72 }}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
placeholder="Email"
|
||||
|
@ -6,13 +6,10 @@ import * as Constants from "~/common/constants";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Styles from "~/common/styles";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { P1 } from "~/components/system";
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { useForm } from "~/common/hooks";
|
||||
import { css } from "@emotion/react";
|
||||
import { SignUpPopover, Verification } from "~/components/core/Auth/components";
|
||||
import { SignUpPopover, Verification, AuthField } from "~/components/core/Auth/components";
|
||||
|
||||
const STYLES_BACK_BUTTON = css`
|
||||
color: ${Constants.semantic.textGrayDark};
|
||||
@ -69,15 +66,8 @@ const STYLES_MESSAGE_PARAGRAPH = (theme) => css`
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE_BUTTON = (theme) => css`
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
svg {
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
${Styles.BUTTON_RESET};
|
||||
color: ${theme.semantic.textGray};
|
||||
`;
|
||||
|
||||
const handleValidation = ({ password }, errors) => {
|
||||
@ -155,7 +145,7 @@ export default function Signin({
|
||||
return (
|
||||
<SignUpPopover title={`Please add an email address for ${emailOrUsername}`}>
|
||||
<form {...getEmailFormProps()} style={{ marginTop: 72 }}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
placeholder="Email"
|
||||
@ -186,13 +176,13 @@ export default function Signin({
|
||||
<form {...getFormProps()} style={{ marginTop: message ? 24 : 41 }}>
|
||||
{message && (
|
||||
<div css={STYLES_MESSAGE}>
|
||||
<P1 css={STYLES_MESSAGE_PARAGRAPH}>{message}</P1>
|
||||
<button css={STYLES_MESSAGE_BUTTON} onClick={clearMessages}>
|
||||
<SVG.Dismiss />
|
||||
<System.P1 css={STYLES_MESSAGE_PARAGRAPH}>{message}</System.P1>
|
||||
<button css={STYLES_MESSAGE_BUTTON} style={{ marginTop: 4 }} onClick={clearMessages}>
|
||||
<SVG.Dismiss height={16} width={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: message ? 24 : 16 }}
|
||||
placeholder="Password"
|
||||
@ -215,7 +205,7 @@ export default function Signin({
|
||||
Sign in
|
||||
</System.ButtonPrimary>
|
||||
<button type="button" onClick={goToResetPassword} css={STYLES_FORGOT_PASSWORD_BUTTON}>
|
||||
<P1 css={Styles.H5}> Forgot Password?</P1>
|
||||
<System.P1 css={Styles.H5}> Forgot Password?</System.P1>
|
||||
</button>
|
||||
</motion.div>
|
||||
</AnimateSharedLayout>
|
||||
@ -223,7 +213,7 @@ export default function Signin({
|
||||
<button css={STYLES_BACK_BUTTON} type="button" onClick={goBack}>
|
||||
<span>
|
||||
<SVG.RightArrow height="16px" style={{ transform: "rotate(180deg)" }} />
|
||||
<P1 css={Styles.H5}>Back</P1>
|
||||
<System.P1 css={Styles.H5}>Back</System.P1>
|
||||
</span>
|
||||
</button>
|
||||
</SignUpPopover>
|
||||
|
@ -4,12 +4,15 @@ import * as Validations from "~/common/validations";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Actions from "~/common/actions";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { useForm } from "~/common/hooks";
|
||||
import { SignUpPopover, Verification, AuthCheckBox } from "~/components/core/Auth/components";
|
||||
import {
|
||||
SignUpPopover,
|
||||
Verification,
|
||||
AuthCheckBox,
|
||||
AuthField,
|
||||
} from "~/components/core/Auth/components";
|
||||
|
||||
const useSignup = () => {
|
||||
const [scene, setScene] = React.useState("verification");
|
||||
@ -94,7 +97,7 @@ export default function Signup({ verifyEmail, createUser, resendEmailVerificatio
|
||||
<SignUpPopover title="Create an account">
|
||||
<AnimateSharedLayout>
|
||||
<form {...getFormProps()}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 46 }}
|
||||
placeholder="Username"
|
||||
@ -122,7 +125,7 @@ export default function Signup({ verifyEmail, createUser, resendEmailVerificatio
|
||||
/>
|
||||
|
||||
<motion.div layout>
|
||||
<Field
|
||||
<AuthField
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
placeholder="Password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
|
@ -4,12 +4,15 @@ import * as Validations from "~/common/validations";
|
||||
import * as System from "~/components/system";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { useForm } from "~/common/hooks";
|
||||
|
||||
import { SignUpPopover, Verification, AuthCheckBox } from "~/components/core/Auth/components";
|
||||
import {
|
||||
SignUpPopover,
|
||||
Verification,
|
||||
AuthCheckBox,
|
||||
AuthField,
|
||||
} from "~/components/core/Auth/components";
|
||||
|
||||
const useTwitterLinking = () => {
|
||||
// NOTE(amine): can be either 'account' | 'email' | 'verificatiom'
|
||||
@ -97,7 +100,7 @@ export default function TwitterLinking({
|
||||
<div>
|
||||
<SignUpPopover title={`Please add an email address for ${username}`}>
|
||||
<form {...getEmailFormProps()} style={{ marginTop: 72 }}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
placeholder="Email"
|
||||
@ -127,7 +130,7 @@ export default function TwitterLinking({
|
||||
return (
|
||||
<SignUpPopover title="Create an account">
|
||||
<form {...getFormProps()}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 41 }}
|
||||
placeholder="Username"
|
||||
@ -137,7 +140,7 @@ export default function TwitterLinking({
|
||||
{...getFieldProps("username")}
|
||||
/>
|
||||
<AnimateSharedLayout>
|
||||
<Field
|
||||
<AuthField
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
containerAs={MotionLayout}
|
||||
errorAs={MotionLayout}
|
||||
|
@ -6,14 +6,17 @@ import * as Actions from "~/common/actions";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { css } from "@emotion/react";
|
||||
import { useForm } from "~/common/hooks";
|
||||
|
||||
import { SignUpPopover, Verification, AuthCheckBox } from "~/components/core/Auth/components";
|
||||
import {
|
||||
SignUpPopover,
|
||||
Verification,
|
||||
AuthCheckBox,
|
||||
AuthField,
|
||||
} from "~/components/core/Auth/components";
|
||||
|
||||
const STYLES_LINK = (theme) => css`
|
||||
padding: 0;
|
||||
@ -124,7 +127,7 @@ export default function TwitterSignup({
|
||||
return (
|
||||
<SignUpPopover title="Create an account">
|
||||
<form {...getFormProps()}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
containerStyle={{ marginTop: 41 }}
|
||||
placeholder="Username"
|
||||
@ -150,7 +153,7 @@ export default function TwitterSignup({
|
||||
full
|
||||
/>
|
||||
<AnimateSharedLayout>
|
||||
<Field
|
||||
<AuthField
|
||||
containerStyle={{ marginTop: 16 }}
|
||||
containerAs={MotionLayout}
|
||||
errorAs={MotionLayout}
|
||||
|
15
components/core/Auth/components/AuthField.js
Normal file
15
components/core/Auth/components/AuthField.js
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from "react";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
const STYLES_INPUT = (theme) => css`
|
||||
background-color: rgba(242, 242, 247, 0.5);
|
||||
box-shadow: ${theme.shadow.lightLarge};
|
||||
border-radius: 12px;
|
||||
`;
|
||||
|
||||
export default function AuthField({ css, ...props }) {
|
||||
return <Field inputCss={[STYLES_INPUT, css]} {...props} />;
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
import * as React from "react";
|
||||
import * as System from "~/components/system";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as Styles from "~/common/styles";
|
||||
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { AnimateSharedLayout, motion } from "framer-motion";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { css } from "@emotion/react";
|
||||
import { useField } from "~/common/hooks";
|
||||
import { SignUpPopover, ArrowButton } from "~/components/core/Auth/components";
|
||||
import { SignUpPopover, ArrowButton, AuthField } from "~/components/core/Auth/components";
|
||||
|
||||
const STYLES_HELPER = (theme) => css`
|
||||
text-align: center;
|
||||
@ -101,16 +98,17 @@ export default function Verification({ onVerify, title = DEFAULT_TITLE, onResend
|
||||
return (
|
||||
<SignUpPopover logoStyle={{ width: 56, height: 56 }} title={title}>
|
||||
<div style={{ marginTop: 40 }}>
|
||||
<Field
|
||||
<AuthField
|
||||
autoFocus
|
||||
label="Enter the 6 digit code sent to your email"
|
||||
full
|
||||
icon={
|
||||
isSubmitting
|
||||
? () => (
|
||||
? ({ style, ...props }) => (
|
||||
<div
|
||||
style={{ width: 24, height: 24, marginRight: 4 }}
|
||||
style={{ width: 20, height: 20, ...style }}
|
||||
css={Styles.CONTAINER_CENTERED}
|
||||
{...props}
|
||||
>
|
||||
<LoaderSpinner height="16px" />
|
||||
</div>
|
||||
|
@ -2,5 +2,6 @@ export { default as Toggle } from "~/components/core/Auth/components/Toggle";
|
||||
export { default as SignUpPopover } from "~/components/core/Auth/components/SignUpPopover";
|
||||
export { default as Verification } from "~/components/core/Auth/components/Verification";
|
||||
export { default as AuthCheckBox } from "~/components/core/Auth/components/AuthCheckBox";
|
||||
export { default as AuthField } from "~/components/core/Auth/components/AuthField";
|
||||
export { default as ArrowButton } from "~/components/core/Auth/components/ArrowButton";
|
||||
export { default as AuthWrapper } from "~/components/core/Auth/components/AuthWrapper";
|
||||
|
@ -103,7 +103,9 @@ export default class CTATransition extends React.Component {
|
||||
const currentURL = Strings.getCurrentURL(this.props.page?.params);
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
href: `/_/auth?tab=signin&email=${emailOrUsername}&redirect=${encodeURI(currentURL)}`,
|
||||
href: `/_/auth?tab=signin&email=${encodeURIComponent(emailOrUsername)}&redirect=${encodeURI(
|
||||
currentURL
|
||||
)}`,
|
||||
});
|
||||
this._handleClose();
|
||||
};
|
||||
@ -112,7 +114,9 @@ export default class CTATransition extends React.Component {
|
||||
const currentURL = Strings.getCurrentURL(this.props.page?.params);
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
href: `/_/auth?tab=signup&email=${email}&redirect=${encodeURI(currentURL)}`,
|
||||
href: `/_/auth?tab=signup&email=${encodeURIComponent(email)}&redirect=${encodeURI(
|
||||
currentURL
|
||||
)}`,
|
||||
});
|
||||
this._handleClose();
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from "react";
|
||||
import * as SVG from "~/common/svg";
|
||||
|
||||
import { P1 } from "~/components/system/components/Typography";
|
||||
import { Input } from "~/components/system";
|
||||
@ -22,14 +21,13 @@ const STYLES_PASSWORD_VALIDATIONS = (theme) => css`
|
||||
border-radius: 12px;
|
||||
`;
|
||||
|
||||
const STYLES_PASSWORD_VALIDATION = (theme) =>
|
||||
css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& > * + * {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`;
|
||||
const STYLES_PASSWORD_VALIDATION = css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& > * + * {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_CIRCLE = (theme) => css`
|
||||
height: 8px;
|
||||
@ -42,29 +40,11 @@ const STYLES_CIRCLE_SUCCESS = (theme) => css`
|
||||
border: 1.25px solid ${theme.system.green};
|
||||
`;
|
||||
|
||||
const STYLES_INPUT = (theme) => css`
|
||||
background-color: rgba(242, 242, 247, 0.5);
|
||||
box-shadow: ${theme.shadow.lightLarge};
|
||||
border-radius: 12px;
|
||||
&::placeholder {
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
}
|
||||
`;
|
||||
const STYLES_INPUT_ERROR = (theme) => css`
|
||||
background-color: rgba(242, 242, 247, 0.5);
|
||||
border: 1px solid ${theme.system.red};
|
||||
border-radius: 8px;
|
||||
&::placeholder {
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
}
|
||||
box-shadow: 0 0 0 1px ${theme.system.red};
|
||||
`;
|
||||
const STYLES_INPUT_SUCCESS = (theme) => css`
|
||||
background-color: rgba(242, 242, 247, 0.5);
|
||||
border: 1px solid ${theme.system.green};
|
||||
border-radius: 8px;
|
||||
&::placeholder {
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
}
|
||||
box-shadow: 0 0 0 1px ${theme.system.green};
|
||||
`;
|
||||
|
||||
const PasswordValidations = ({ validations }) => {
|
||||
@ -103,15 +83,16 @@ export default function Field({
|
||||
validations,
|
||||
errorAs,
|
||||
containerAs,
|
||||
inputCss,
|
||||
...props
|
||||
}) {
|
||||
const showError = touched && error;
|
||||
const showSuccess = touched && !error;
|
||||
|
||||
const STYLES = React.useMemo(() => {
|
||||
const STYLES_VALIDATIONS = React.useMemo(() => {
|
||||
if (showError) return STYLES_INPUT_ERROR;
|
||||
if (showSuccess) return STYLES_INPUT_SUCCESS;
|
||||
return STYLES_INPUT;
|
||||
return null;
|
||||
}, [touched, error]);
|
||||
|
||||
const ContainerComponent = containerAs || "div";
|
||||
@ -119,7 +100,7 @@ export default function Field({
|
||||
return (
|
||||
<div>
|
||||
<ContainerComponent>
|
||||
<Input inputCss={STYLES} {...props} />
|
||||
<Input inputCss={[inputCss, STYLES_VALIDATIONS]} {...props} />
|
||||
</ContainerComponent>
|
||||
{props.name === "password" && validations ? (
|
||||
<PasswordValidations validations={validations} />
|
||||
|
@ -67,11 +67,11 @@ const STYLES_FILTERS_GROUP = css`
|
||||
`;
|
||||
|
||||
const STYLES_ICON_CONTAINER = css`
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 2px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
`;
|
||||
|
||||
const FilterButton = React.forwardRef(({ children, Icon, image, isSelected, ...props }, ref) => (
|
||||
@ -83,7 +83,7 @@ const FilterButton = React.forwardRef(({ children, Icon, image, isSelected, ...p
|
||||
</div>
|
||||
) : null}
|
||||
{image ? image : null}
|
||||
<Typography.P2 as="span" nbrOflines={1} style={{ marginLeft: 6 }}>
|
||||
<Typography.P2 as="span" nbrOflines={1} style={{ marginLeft: 6, color: "inherit" }}>
|
||||
{children}
|
||||
</Typography.P2>
|
||||
</span>
|
||||
|
@ -12,6 +12,12 @@ import { css } from "@emotion/react";
|
||||
* Popup trigger
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
|
||||
const STYLES_POPUP_TRIGGER = (theme) => css`
|
||||
${Styles.HORIZONTAL_CONTAINER_CENTERED};
|
||||
perspective: 1000px;
|
||||
color: ${theme.semantic.textBlack};
|
||||
`;
|
||||
|
||||
export function PopupTrigger({ children, isMobile, ...props }) {
|
||||
const [{ sidebarState, popupState }, { hidePopup, togglePopup }] = useFilterContext();
|
||||
|
||||
@ -26,11 +32,13 @@ export function PopupTrigger({ children, isMobile, ...props }) {
|
||||
return (
|
||||
<System.ButtonPrimitive
|
||||
onClick={togglePopup}
|
||||
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
|
||||
css={STYLES_POPUP_TRIGGER}
|
||||
//NOTE(amine): fix to a bug where elements using rotate disappear in safari https://stackoverflow.com/questions/22621544/webkit-transform-breaks-z-index-on-safari
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<motion.div
|
||||
style={{ color: "inherit" }}
|
||||
initial={{ rotateX: popupState.isVisible ? 0 : 180 }}
|
||||
animate={{ rotateX: popupState.isVisible ? 0 : 180 }}
|
||||
>
|
||||
|
@ -5,6 +5,8 @@ import * as Search from "~/components/core/Search";
|
||||
import * as Filters from "~/components/core/Filter/Filters";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { Provider } from "~/components/core/Filter/Provider";
|
||||
import { Sidebar, SidebarTrigger } from "~/components/core/Filter/Sidebar";
|
||||
@ -38,6 +40,8 @@ function Title({ page, data }) {
|
||||
if (page.id === "NAV_DATA") return "Recent";
|
||||
if (page.id === "NAV_SLATE" && data?.slatename) return "# " + data?.slatename;
|
||||
if (page.id === "NAV_PROFILE") return "@ " + data.username;
|
||||
if (page.id === "NAV_SETTINGS") return page.name;
|
||||
if (page.id === "NAV_API") return page.name;
|
||||
|
||||
return null;
|
||||
}, [page, data, isFetchingResults, isSearching]);
|
||||
@ -147,7 +151,12 @@ export default function Filter({
|
||||
|
||||
if (disabled) {
|
||||
return showSearchResult ? (
|
||||
<Search.Content viewer={viewer} page={page} onAction={onAction} isMobile={isMobile} />
|
||||
<WebsitePrototypeWrapper
|
||||
title={`${page.pageTitle} • Slate`}
|
||||
url={`${Constants.hostname}${page.pathname}`}
|
||||
>
|
||||
<Search.Content viewer={viewer} page={page} onAction={onAction} isMobile={isMobile} />
|
||||
</WebsitePrototypeWrapper>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
@ -169,7 +178,7 @@ export default function Filter({
|
||||
>
|
||||
<div css={STYLES_NAVBAR}>
|
||||
<div css={STYLES_NAVBAR_BACKGROUND} />
|
||||
<div css={STYLES_FILTER_POPUP_WRAPPER}>
|
||||
<div css={STYLES_FILTER_POPUP_WRAPPER} style={{ perspective: "1000px" }}>
|
||||
<Popup
|
||||
viewer={viewer}
|
||||
onAction={onAction}
|
||||
@ -210,7 +219,12 @@ export default function Filter({
|
||||
/>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
{showSearchResult ? (
|
||||
<Search.Content viewer={viewer} page={page} onAction={onAction} isMobile={isMobile} />
|
||||
<WebsitePrototypeWrapper
|
||||
title={`${page.pageTitle} • Slate`}
|
||||
url={`${Constants.hostname}${page.pathname}`}
|
||||
>
|
||||
<Search.Content viewer={viewer} page={page} onAction={onAction} isMobile={isMobile} />
|
||||
</WebsitePrototypeWrapper>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
|
@ -63,6 +63,7 @@ const STYLES_MOBILE_SHOW_CONTROLS = (theme) => css`
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 1px ${theme.system.grayLight5}, ${theme.shadow.lightLarge};
|
||||
color: ${theme.semantic.textBlack};
|
||||
|
||||
background-color: ${theme.semantic.bgWhite};
|
||||
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
|
||||
|
@ -14,6 +14,7 @@ const STYLES_BUTTON = (theme) => css`
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 1px ${theme.system.grayLight5}, ${theme.shadow.lightLarge};
|
||||
transition: box-shadow 0.3s;
|
||||
color: ${theme.semantic.textBlack};
|
||||
:hover {
|
||||
box-shadow: 0 0 0 1px ${theme.system.pink}, ${theme.shadow.lightLarge};
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const STYLES_BUTTON_HOVER = (theme) => css`
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 1px ${theme.system.grayLight5}, ${theme.shadow.lightLarge};
|
||||
transition: box-shadow 0.3s;
|
||||
color: ${theme.semantic.textBlack};
|
||||
:hover {
|
||||
box-shadow: 0 0 0 1px ${theme.system.pink}, ${theme.shadow.lightLarge};
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const STYLES_BUTTON = (theme) => css`
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 1px ${theme.system.grayLight5}, ${theme.shadow.lightLarge};
|
||||
transition: box-shadow 0.3s;
|
||||
color: ${theme.semantic.textBlack};
|
||||
:hover {
|
||||
box-shadow: 0 0 0 1px ${theme.system.blue}, ${theme.shadow.lightLarge};
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { Input as InputPrimitive } from "~/components/system/components/Input";
|
||||
import { useSearchStore } from "~/components/core/Search/store";
|
||||
import { FileTypeGroup } from "~/components/core/FileTypeIcon";
|
||||
import { Link } from "~/components/core/Link";
|
||||
import { useIsomorphicLayoutEffect } from "~/common/hooks";
|
||||
|
||||
import DataView from "~/components/core/DataView";
|
||||
import CollectionPreviewBlock from "~/components/core/CollectionPreviewBlock";
|
||||
@ -126,6 +127,12 @@ function Input({ viewer, data, page, onAction }) {
|
||||
|
||||
const handleChange = useDebouncedOnChange({ setQuery, handleSearch });
|
||||
|
||||
// NOTE(amine): cleanup input's value when query is empty.
|
||||
const inputRef = React.useRef();
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (!query && inputRef.current) inputRef.current.value = "";
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@ -140,6 +147,7 @@ function Input({ viewer, data, page, onAction }) {
|
||||
}}
|
||||
>
|
||||
<InputPrimitive
|
||||
ref={inputRef}
|
||||
full
|
||||
containerStyle={{ height: "100%" }}
|
||||
inputCss={STYLES_SEARCH_COMPONENT}
|
||||
|
@ -248,7 +248,7 @@ export function MobileUploadJumper({ data }) {
|
||||
</div>
|
||||
<MobileJumper.Divider />
|
||||
<div style={{ padding: "40px 16px", textAlign: "center" }}>
|
||||
<SVG.Airplay color="textBlack" />
|
||||
<SVG.Airplay style={{ color: Constants.semantic.textGrayDark }} />
|
||||
<System.H5 as="p" color="textGrayDark" style={{ marginTop: 6, textAlign: "center" }}>
|
||||
open Slate on desktop to upload files
|
||||
</System.H5>
|
||||
|
@ -28,6 +28,14 @@ const STYLES_POPUP_WRAPPER = (theme) => css`
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
z-index: ${theme.zindex.tooltip};
|
||||
border-radius: 12px;
|
||||
box-shadow: ${theme.shadow.lightLarge};
|
||||
`;
|
||||
|
||||
const STYLES_POPUP_CAROUSEL_POSITION = (theme) => css`
|
||||
@media (max-width: ${theme.sizes.mobile}px) {
|
||||
bottom: ${Constants.sizes.carouselMobileFooterHeight + 16}px;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DISMISS_BUTTON = (theme) => css`
|
||||
@ -59,7 +67,7 @@ const STYLES_POPUP_CONTENT = css`
|
||||
|
||||
const useUploadPopup = ({ totalFilesSummary }) => {
|
||||
const {
|
||||
state: { isFinished },
|
||||
state: { isFinished, isUploading },
|
||||
handlers: { resetUploadState },
|
||||
} = useUploadStore();
|
||||
|
||||
@ -75,12 +83,11 @@ const useUploadPopup = ({ totalFilesSummary }) => {
|
||||
|
||||
const timeoutRef = React.useRef();
|
||||
//NOTE(amine): show the upload summary, then automatically collapse the upload summary after 3 seconds
|
||||
const isStarted = totalFilesSummary.total > 0;
|
||||
React.useEffect(() => {
|
||||
if (!isStarted) return;
|
||||
if (!isUploading) return;
|
||||
expandUploadSummary();
|
||||
timeoutRef.current = setTimeout(collapseUploadSummary, 3000);
|
||||
}, [isStarted]);
|
||||
}, [isUploading]);
|
||||
|
||||
/**
|
||||
* NOTE(amine): show the upload summary when a file fails to upload or is added to the queue,
|
||||
@ -89,7 +96,7 @@ const useUploadPopup = ({ totalFilesSummary }) => {
|
||||
const isSummaryExpandedRef = React.useRef();
|
||||
isSummaryExpandedRef.current = popupState.isSummaryExpanded;
|
||||
React.useEffect(() => {
|
||||
if (isSummaryExpandedRef.current || totalFilesSummary.total === 0) return;
|
||||
if (isSummaryExpandedRef.current || totalFilesSummary.total === 0 || !isUploading) return;
|
||||
expandUploadSummary();
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = setTimeout(collapseUploadSummary, 3000);
|
||||
@ -160,7 +167,7 @@ const useUploadSummary = ({ fileLoading }) =>
|
||||
};
|
||||
}, [fileLoading]);
|
||||
|
||||
export function Popup({ isMobile }) {
|
||||
export function Popup({ page, isMobile }) {
|
||||
const {
|
||||
state: { isFinished, fileLoading },
|
||||
handlers: { resetUploadState },
|
||||
@ -175,11 +182,12 @@ export function Popup({ isMobile }) {
|
||||
{ hideUploadPopup, expandUploadSummary, collapseUploadSummary, cancelAutoCollapseOnMouseEnter },
|
||||
] = useUploadPopup({ totalFilesSummary });
|
||||
|
||||
const isGlobalCarouselOpen = !!page.params.id;
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{popupState.isVisible ? (
|
||||
<motion.div
|
||||
css={STYLES_POPUP_WRAPPER}
|
||||
css={[STYLES_POPUP_WRAPPER, isGlobalCarouselOpen && STYLES_POPUP_CAROUSEL_POSITION]}
|
||||
initial={{ opacity: 0, y: 0 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
@ -190,9 +198,9 @@ export function Popup({ isMobile }) {
|
||||
<AnimatePresence>
|
||||
{popupState.isSummaryExpanded ? (
|
||||
<motion.div
|
||||
initial={{ y: 400 }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: 400 }}
|
||||
initial={{ willChange: "height", height: 0 }}
|
||||
animate={{ height: "auto" }}
|
||||
exit={{ height: 0 }}
|
||||
transition={{ type: "spring", stiffness: 170, damping: 26 }}
|
||||
onMouseEnter={cancelAutoCollapseOnMouseEnter}
|
||||
>
|
||||
@ -304,7 +312,11 @@ function Header({ totalFilesSummary, popupState, expandUploadSummary, collapseUp
|
||||
aria-label="Upload Summary"
|
||||
onClick={popupState.isSummaryExpanded ? collapseUploadSummary : expandUploadSummary}
|
||||
>
|
||||
<span css={Styles.HORIZONTAL_CONTAINER_CENTERED}>
|
||||
<span
|
||||
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
|
||||
//NOTE(amine): fix to a bug where elements using rotate disappear in safari https://stackoverflow.com/questions/22621544/webkit-transform-breaks-z-index-on-safari
|
||||
style={{ perspective: "1000px" }}
|
||||
>
|
||||
<System.P2 color="textBlack" style={{ width: "5ch" }}>
|
||||
{uploadProgress}%
|
||||
</System.P2>
|
||||
|
@ -14,13 +14,13 @@ import DropIndicator from "~/components/core/Upload/DropIndicator";
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Root
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
const Root = ({ children, data, isMobile }) => {
|
||||
const Root = ({ children, page, data, isMobile }) => {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
{isMobile ? <MobileUploadJumper data={data} /> : <UploadJumper data={data} />}
|
||||
<ModalPortal>
|
||||
<Popup isMobile={isMobile} />
|
||||
<Popup page={page} isMobile={isMobile} />
|
||||
<DropIndicator data={data} />
|
||||
</ModalPortal>
|
||||
</>
|
||||
|
@ -128,10 +128,13 @@ const STYLES_CODE_BLOCK = css`
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
padding: 20px 24px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
|
||||
* {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
width: 0;
|
||||
@ -193,7 +196,7 @@ const STYLES_LINE = css`
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const STYLES_TOPBAR = css`
|
||||
const STYLES_TOPBAR = (theme) => css`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@ -204,6 +207,11 @@ const STYLES_TOPBAR = css`
|
||||
border-top-left-radius: 4px;
|
||||
margin-bottom: -4px;
|
||||
box-sizing: border-box;
|
||||
@media (max-width: ${theme.sizes.mobile}px) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_TOPBAR_PLAIN = css`
|
||||
@ -220,6 +228,7 @@ const STYLES_TOPBAR_PLAIN = css`
|
||||
`;
|
||||
|
||||
const STYLES_TOPBAR_TITLE = css`
|
||||
flex-grow: 1;
|
||||
text-transform: uppercase;
|
||||
color: ${Constants.semantic.textGray};
|
||||
font-size: ${Constants.typescale.lvlN1};
|
||||
@ -229,7 +238,6 @@ const STYLES_TOPBAR_TITLE = css`
|
||||
`;
|
||||
|
||||
const STYLES_LANGSWITCHER = css`
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
@ -50,7 +50,7 @@ const VisitLinkButton = ({ file }) => {
|
||||
type="link"
|
||||
>
|
||||
<LinkIcon file={file} width={16} height={16} style={{ marginRight: 4 }} key={file.id} />
|
||||
<span style={{ whiteSpace: "nowrap" }}>Visit site</span>
|
||||
<span style={{ whiteSpace: "nowrap", color: "inherit" }}>Visit site</span>
|
||||
</System.ButtonTertiary>
|
||||
);
|
||||
};
|
||||
@ -77,7 +77,8 @@ export const useSaveHandler = ({ file, viewer, onAction }) => {
|
||||
}
|
||||
|
||||
if (isSaved) {
|
||||
const fileLibraryURL = `/_/data?id=${file.id}`;
|
||||
const fileId = viewer.library.find((item) => item.cid === file.cid).id;
|
||||
const fileLibraryURL = `/_/data?id=${fileId}`;
|
||||
onAction({
|
||||
type: "NAVIGATE",
|
||||
href: fileLibraryURL,
|
||||
@ -160,6 +161,7 @@ const STYLES_HEADER_WRAPPER = (theme) => css`
|
||||
const STYLES_ACTION_BUTTON = (theme) => css`
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: ${theme.semantic.bgGrayLight};
|
||||
@ -565,8 +567,6 @@ function CarouselHeaderMobile({
|
||||
);
|
||||
}
|
||||
|
||||
const MOBILE_FOOTER_HEIGHT = 52;
|
||||
|
||||
const STYLES_CAROUSEL_MOBILE_FOOTER = (theme) => css`
|
||||
${Styles.HORIZONTAL_CONTAINER_CENTERED};
|
||||
position: fixed;
|
||||
@ -577,7 +577,7 @@ const STYLES_CAROUSEL_MOBILE_FOOTER = (theme) => css`
|
||||
padding: 8px 16px;
|
||||
border-top: 1px solid ${theme.semantic.borderGrayLight};
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
height: ${MOBILE_FOOTER_HEIGHT}px;
|
||||
height: ${Constants.sizes.carouselMobileFooterHeight}px;
|
||||
|
||||
background-color: ${theme.semantic.bgWhite};
|
||||
@supports ((-webkit-backdrop-filter: blur(15px)) or (backdrop-filter: blur(15px))) {
|
||||
@ -637,7 +637,7 @@ function CarouselFooterMobile({ file, onAction, external, isOwner, data, viewer
|
||||
<AnimatePresence>
|
||||
{isEditInfoVisible && (
|
||||
<Jumpers.EditInfoMobile
|
||||
footerStyle={{ bottom: MOBILE_FOOTER_HEIGHT }}
|
||||
footerStyle={{ bottom: Constants.sizes.carouselMobileFooterHeight }}
|
||||
file={file}
|
||||
onClose={hideEditInfo}
|
||||
/>
|
||||
@ -773,6 +773,7 @@ const STYLES_CONTROLS_BUTTON = (theme) => css`
|
||||
border: 1px solid ${theme.semantic.borderGrayLight};
|
||||
padding: 10px;
|
||||
box-shadow: ${theme.shadow.lightMedium};
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
|
@ -285,6 +285,7 @@ const STYLES_APPLIED_SLATE_BUTTON = (theme) => css`
|
||||
background-color: ${theme.semantic.bgWhite};
|
||||
border: 1px solid ${theme.semantic.borderGrayLight};
|
||||
box-shadow: ${theme.shadow.lightSmall};
|
||||
color: ${theme.semantic.textBlack};
|
||||
`;
|
||||
|
||||
const STYLES_APPLIED_COLOR_TEXTBLACK = (theme) => css`
|
||||
@ -423,8 +424,9 @@ const STYLES_SLATES_MENU_BUTTON_SELECTED = (theme) => css`
|
||||
background-color: ${theme.semantic.bgGrayLight4};
|
||||
`;
|
||||
|
||||
const STYLES_SLATES_MENU_BUTTON = css`
|
||||
const STYLES_SLATES_MENU_BUTTON = (theme) => css`
|
||||
${Styles.HORIZONTAL_CONTAINER_CENTERED};
|
||||
color: ${theme.semantic.textBlack};
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 9px 8px 11px;
|
||||
@ -459,9 +461,9 @@ const STYLES_SLATES_MENU_BUTTON_BLUE = (theme) => css`
|
||||
}
|
||||
`;
|
||||
const STYLES_SLATES_MENU_BUTTON_TEXTBLACK = (theme) => css`
|
||||
color: ${theme.system.textBlack};
|
||||
color: ${theme.semantic.textBlack};
|
||||
&:hover {
|
||||
color: ${theme.system.textBlack};
|
||||
color: ${theme.semantic.textBlack};
|
||||
}
|
||||
`;
|
||||
|
||||
@ -486,21 +488,21 @@ const ComboboxSlatesMenuButton = ({
|
||||
index={index}
|
||||
{...props}
|
||||
>
|
||||
<div>
|
||||
{hasPublicIcon && (
|
||||
<div style={{ position: "absolute", padding: "2px", left: "8px", top: "9x" }}>
|
||||
<SVG.Users width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
<System.H5
|
||||
as="span"
|
||||
nbrOflines={isCreateAction ? 2 : 1}
|
||||
title={children}
|
||||
style={{ marginLeft: 32, maxWidth: "46ch" }}
|
||||
{hasPublicIcon && (
|
||||
<div
|
||||
style={{ position: "absolute", color: "inherit", padding: "2px", left: "8px", top: "9x" }}
|
||||
>
|
||||
{children}
|
||||
</System.H5>
|
||||
</div>
|
||||
<SVG.Users width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
<System.H5
|
||||
as="span"
|
||||
nbrOflines={isCreateAction ? 2 : 1}
|
||||
title={children}
|
||||
style={{ marginLeft: 32, maxWidth: "46ch", color: "inherit" }}
|
||||
>
|
||||
{children}
|
||||
</System.H5>
|
||||
{!isCreateAction && isSelected && (
|
||||
<div css={Styles.HORIZONTAL_CONTAINER_CENTERED} style={{ marginLeft: "auto" }}>
|
||||
<System.P3 color="textGrayDark">{isSlateApplied ? "remove tag" : "apply tag"}</System.P3>
|
||||
|
@ -228,8 +228,11 @@ function CoverImageUpload({ file, viewer, isMobile, isFileOwner }) {
|
||||
onChange={handleInputChange}
|
||||
tabIndex="-1"
|
||||
/>
|
||||
<SVG.UploadCloud width={16} />
|
||||
<System.P3 style={{ maxWidth: 140, textAlign: "center", marginTop: 8 }}>
|
||||
<SVG.UploadCloud style={{ color: Constants.semantic.textBlack }} width={16} />
|
||||
<System.P3
|
||||
color="textBlack"
|
||||
style={{ maxWidth: 140, textAlign: "center", marginTop: 8 }}
|
||||
>
|
||||
{isMobile
|
||||
? "Select an image as object preview"
|
||||
: "Drop or select an image as object preview"}
|
||||
@ -288,7 +291,7 @@ function FileMetadata({ file, ...props }) {
|
||||
style={{ marginTop: 6, padding: "4px 0px", justifyContent: "space-between" }}
|
||||
>
|
||||
<System.P3 color="textGray">Type</System.P3>
|
||||
<System.P3>{Strings.capitalize(file.type)}</System.P3>
|
||||
<System.P3 color="textBlack">{Strings.capitalize(file.type)}</System.P3>
|
||||
</div>
|
||||
<System.Divider
|
||||
color="borderGrayLight4"
|
||||
@ -300,7 +303,7 @@ function FileMetadata({ file, ...props }) {
|
||||
style={{ padding: "4px 0px", justifyContent: "space-between" }}
|
||||
>
|
||||
<System.P3 color="textGray">Size</System.P3>
|
||||
<System.P3>{Strings.bytesToSize(file.size, 0)}</System.P3>
|
||||
<System.P3 color="textBlack">{Strings.bytesToSize(file.size, 0)}</System.P3>
|
||||
</div>
|
||||
{Validations.isPreviewableImage(file?.type || "") ? (
|
||||
<>
|
||||
@ -314,7 +317,7 @@ function FileMetadata({ file, ...props }) {
|
||||
style={{ padding: "4px 0px", justifyContent: "space-between" }}
|
||||
>
|
||||
<System.P3 color="textGray">Dimension</System.P3>
|
||||
<System.P3>
|
||||
<System.P3 color="textBlack">
|
||||
<ImageDimension file={file} />
|
||||
</System.P3>
|
||||
</div>
|
||||
@ -330,7 +333,7 @@ function FileMetadata({ file, ...props }) {
|
||||
style={{ padding: "4px 0px", justifyContent: "space-between" }}
|
||||
>
|
||||
<System.P3 color="textGray">Created</System.P3>
|
||||
<System.P3>{Utilities.formatDateToString(file.createdAt)}</System.P3>
|
||||
<System.P3 color="textBlack">{Utilities.formatDateToString(file.createdAt)}</System.P3>
|
||||
</div>
|
||||
<System.Divider
|
||||
color="borderGrayLight4"
|
||||
@ -346,6 +349,7 @@ function FileMetadata({ file, ...props }) {
|
||||
>
|
||||
<System.P3 color="textGray">CID</System.P3>
|
||||
<System.P3
|
||||
color="textBlack"
|
||||
style={{
|
||||
wordBreak: "break-all",
|
||||
textAlign: "right",
|
||||
|
@ -19,6 +19,7 @@ const STYLES_SHARING_BUTTON = (theme) => css`
|
||||
padding: 9px 8px 11px;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
color: ${theme.semantic.textBlack};
|
||||
:hover,
|
||||
:active {
|
||||
background-color: ${theme.semantic.bgGrayLight};
|
||||
@ -26,6 +27,9 @@ const STYLES_SHARING_BUTTON = (theme) => css`
|
||||
:hover {
|
||||
color: ${theme.semantic.textBlack};
|
||||
}
|
||||
* {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@media (max-width: ${theme.sizes.mobile}px) {
|
||||
padding: 9px 0px 11px;
|
||||
@ -63,21 +67,29 @@ function FileSharingButtons({ file, data, viewer }) {
|
||||
return (
|
||||
<>
|
||||
<System.ButtonPrimitive css={STYLES_SHARING_BUTTON} onClick={handleTwitterSharing} autoFocus>
|
||||
<SVG.Twitter width={20} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<SVG.Twitter width={16} height={16} />
|
||||
</div>
|
||||
<System.P2 style={{ marginLeft: 12 }}>Share via Twitter</System.P2>
|
||||
</System.ButtonPrimitive>
|
||||
<System.ButtonPrimitive css={STYLES_SHARING_BUTTON} onClick={handleEmailSharing}>
|
||||
<SVG.Mail width={20} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<SVG.Mail width={16} height={16} />
|
||||
</div>
|
||||
<System.P2 style={{ marginLeft: 12 }}>Share via email </System.P2>
|
||||
</System.ButtonPrimitive>
|
||||
<System.ButtonPrimitive css={STYLES_SHARING_BUTTON} onClick={handleLinkCopy}>
|
||||
<SVG.Link width={20} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<SVG.Link width={16} height={16} />
|
||||
</div>
|
||||
<System.P2 style={{ marginLeft: 12 }}>
|
||||
{copyState.isLinkCopied ? "Copied" : "Copy public link"}
|
||||
</System.P2>
|
||||
</System.ButtonPrimitive>
|
||||
<System.ButtonPrimitive css={STYLES_SHARING_BUTTON} onClick={handleCidCopy}>
|
||||
<SVG.Hexagon width={20} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<SVG.Hexagon width={16} height={16} />
|
||||
</div>
|
||||
<System.P2 style={{ marginLeft: 12 }}>
|
||||
{copyState.isCidCopied ? "Copied" : "Copy CID "}
|
||||
</System.P2>
|
||||
@ -116,9 +128,13 @@ function DownloadButton({ file, viewer, ...props }) {
|
||||
<div ref={downloadRef}>
|
||||
<System.ButtonPrimitive css={STYLES_SHARING_BUTTON} onClick={handleDownload} {...props}>
|
||||
{isDownloading ? (
|
||||
<LoaderSpinner style={{ height: 16, width: 16 }} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<LoaderSpinner style={{ height: 16, width: 16 }} />
|
||||
</div>
|
||||
) : (
|
||||
<SVG.Download width={16} />
|
||||
<div style={{ padding: 2 }}>
|
||||
<SVG.Download width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
<System.P2 style={{ marginLeft: 12 }}>Download file</System.P2>
|
||||
</System.ButtonPrimitive>
|
||||
@ -163,7 +179,9 @@ export function Share({ file, data, viewer, onClose }) {
|
||||
rel="noreferrer"
|
||||
>
|
||||
<SVG.InfoCircle width={16} />
|
||||
<System.P2 style={{ marginLeft: 4 }}>What is a CID?</System.P2>
|
||||
<System.P2 color="textGrayDark" style={{ marginLeft: 4 }}>
|
||||
What is a CID?
|
||||
</System.P2>
|
||||
</a>
|
||||
</Jumper.Item>
|
||||
</Jumper.Root>
|
||||
|
@ -5,7 +5,7 @@ import * as Strings from "~/common/strings";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { DescriptionGroup } from "~/components/system/components/fragments/DescriptionGroup";
|
||||
import { FocusRing } from "~/components/core/FocusRing";
|
||||
import { mergeRefs } from "~/common/utilities";
|
||||
|
||||
const INPUT_STYLES = (theme) => css`
|
||||
box-sizing: border-box;
|
||||
@ -70,7 +70,7 @@ const STYLES_INPUT = css`
|
||||
justify-content: flex-start;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
padding: 0px 12px;
|
||||
background: ${Constants.system.white};
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -130,21 +130,25 @@ const INPUT_COLOR_MAP = {
|
||||
|
||||
class InputPrimitive extends React.Component {
|
||||
_unit;
|
||||
_input;
|
||||
_isPin = this.props.type === "pin";
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._input = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
if (this.props.unit) {
|
||||
this._input.style.paddingRight = `${this._unit.offsetWidth + 48}px`;
|
||||
this._input.current.style.paddingRight = `${this._unit.offsetWidth + 48}px`;
|
||||
}
|
||||
|
||||
if (this.props.autoFocus) {
|
||||
this._input.focus();
|
||||
this._input.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
_handleCopy = (e) => {
|
||||
this._input.select();
|
||||
this._input.current.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
@ -240,12 +244,7 @@ class InputPrimitive extends React.Component {
|
||||
}}
|
||||
>
|
||||
<input
|
||||
ref={(c) => {
|
||||
this._input = c;
|
||||
if (this.props.innerRef) {
|
||||
this.props.innerRef?.(c);
|
||||
}
|
||||
}}
|
||||
ref={mergeRefs([this._input, this.props.innerRef])}
|
||||
css={[INPUT_STYLES, this._isPin && STYLES_PIN_INPUT]}
|
||||
autoFocus={this.props.autoFocus}
|
||||
value={this._isPin ? this._formatPin(this.props.value) : this.props.value}
|
||||
@ -256,7 +255,7 @@ class InputPrimitive extends React.Component {
|
||||
onFocus={
|
||||
this.props.autoHighlight
|
||||
? () => {
|
||||
this._input.select();
|
||||
this._input.current.select();
|
||||
}
|
||||
: this.props.onFocus
|
||||
}
|
||||
|
@ -108,10 +108,11 @@ function Header({ children, style, ...props }) {
|
||||
* Dismiss
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
|
||||
const STYLES_DISMISS_BUTTON = css`
|
||||
const STYLES_DISMISS_BUTTON = (theme) => css`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-left: 12px;
|
||||
color: ${theme.semantic.textGrayDark};
|
||||
`;
|
||||
|
||||
const Dismiss = React.forwardRef(({ css, ...props }, ref) => {
|
||||
|
@ -9,11 +9,11 @@ import * as Strings from "~/common/strings";
|
||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
import WebsiteHeader from "~/components/core/WebsiteHeader";
|
||||
import WebsiteFooter from "~/components/core/WebsiteFooter";
|
||||
import Field from "~/components/core/Field";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { useForm } from "~/common/hooks";
|
||||
import { motion, AnimateSharedLayout } from "framer-motion";
|
||||
import { AuthField } from "~/components/core/Auth/components";
|
||||
|
||||
const INTEGRATION = [
|
||||
{
|
||||
@ -847,7 +847,7 @@ export default function IndexPage() {
|
||||
return errors;
|
||||
},
|
||||
onSubmit: async ({ email }) => {
|
||||
window.open(`/_/auth?tab=signup&email=${email}`, "_self");
|
||||
window.open(`/_/auth?tab=signup&email=${encodeURIComponent(email)}`, "_self");
|
||||
},
|
||||
});
|
||||
|
||||
@ -1084,7 +1084,7 @@ export default function IndexPage() {
|
||||
</System.ButtonPrimaryFull>
|
||||
<div css={STYLES_DIVIDER} />
|
||||
<form {...getSigninFormProps()} style={{ width: "100%" }}>
|
||||
<Field
|
||||
<AuthField
|
||||
{...getSignupFielProps("email")}
|
||||
label="Sign up with email"
|
||||
placeholder="Email"
|
||||
|
@ -161,10 +161,9 @@ const AuthScene = ({ onAuthenticate, onTwitterAuthenticate, page, onAction, ...p
|
||||
);
|
||||
}
|
||||
// NOTE(amine): if the user goes back, we should prefill the email
|
||||
const emailParam = page?.params?.email ? decodeURIComponent(page?.params?.email) : "";
|
||||
const initialEmail =
|
||||
prevScene === "signin" && context.emailOrUsername
|
||||
? context.emailOrUsername
|
||||
: page?.params?.email || "";
|
||||
prevScene === "signin" && context.emailOrUsername ? context.emailOrUsername : emailParam;
|
||||
|
||||
return (
|
||||
<Initial
|
||||
|
@ -48,7 +48,8 @@ const STYLES_API_KEY = css`
|
||||
background-color: ${Constants.semantic.bgLight};
|
||||
outline: none;
|
||||
border: none;
|
||||
width: 380px;
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
font-family: ${Constants.font.code};
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
|
@ -68,7 +68,7 @@ const STYLES_SECURITY_LOCK_WRAPPER = (theme) => css`
|
||||
`;
|
||||
|
||||
const STYLES_EMPTY_STATE_WRAPPER = (theme) => css`
|
||||
// NOTE(amine): 100vh - headers' height - Dataviewer's bottom padding
|
||||
// NOTE(amine): 100vh - headers' height - Dataview's bottom padding
|
||||
height: calc(100vh - ${theme.sizes.filterNavbar + theme.sizes.header}px - 44px);
|
||||
margin: 20px;
|
||||
@media (max-width: ${theme.sizes.mobile}px) {
|
||||
@ -509,7 +509,9 @@ class SlatePage extends React.Component {
|
||||
</EmptyState>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyState>There's nothing here :)</EmptyState>
|
||||
<div css={Styles.PAGE_EMPTY_STATE_WRAPPER}>
|
||||
<EmptyState>There's nothing here :)</EmptyState>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
ref={(c) => {
|
||||
|
Loading…
Reference in New Issue
Block a user