finished data view edit for list view

This commit is contained in:
Martina 2020-09-23 13:52:00 -07:00
parent b140a10013
commit 2d513572f2
22 changed files with 586 additions and 253 deletions

View File

@ -133,6 +133,13 @@ export const processPendingFiles = async (data) => {
});
};
export const addFileToSlate = async (data) => {
return await returnJSON(`/api/slates/add-url`, {
...DEFAULT_OPTIONS,
body: JSON.stringify(data),
});
};
export const updateViewer = async (data) => {
return await returnJSON(`/api/users/update`, {
...DEFAULT_OPTIONS,

View File

@ -29,6 +29,7 @@ export const system = {
slate: "#27292e",
moonstone: "#807d78",
wall: "#cfced3",
shadow: "rgba(15, 14, 18, 0.03)",
};
export const zindex = {

View File

@ -37,6 +37,26 @@ export const Directory = (props) => {
);
};
export const PlusCircle = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
height={props.height}
style={props.style}
>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" />
<path d="M12 8V16" />
<path d="M8 12H16" />
</svg>
);
};
export const Users = (props) => {
return (
<svg
@ -915,6 +935,21 @@ export const Plus = (props) => (
</svg>
);
export const Minus = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M5 12H19" />
</svg>
);
export const FilecoinLogo = (props) => (
<svg
viewBox="0 0 127 127"

View File

@ -75,16 +75,6 @@ export const slatename = (text) => {
return true;
};
export const email = (text) => {
if (!text || !text.length) {
return false;
}
if (!EMAIL_REGEX.test(text)) {
return false;
}
return true;
};
export const username = (text) => {
if (Strings.isEmpty(text)) {
return false;

View File

@ -39,6 +39,7 @@ import SidebarCreateWalletAddress from "~/components/sidebars/SidebarCreateWalle
import SidebarWalletSendFunds from "~/components/sidebars/SidebarWalletSendFunds";
import SidebarFileStorageDeal from "~/components/sidebars/SidebarFileStorageDeal";
import SidebarAddFileToBucket from "~/components/sidebars/SidebarAddFileToBucket";
import SidebarAddFileToSlate from "~/components/sidebars/SidebarAddFileToSlate";
import SidebarDragDropNotice from "~/components/sidebars/SidebarDragDropNotice";
import SidebarSingleSlateSettings from "~/components/sidebars/SidebarSingleSlateSettings";
import SidebarFilecoinArchive from "~/components/sidebars/SidebarFilecoinArchive";
@ -64,6 +65,7 @@ const SIDEBARS = {
SIDEBAR_WALLET_SEND_FUNDS: <SidebarWalletSendFunds />,
SIDEBAR_CREATE_WALLET_ADDRESS: <SidebarCreateWalletAddress />,
SIDEBAR_ADD_FILE_TO_BUCKET: <SidebarAddFileToBucket />,
SIDEBAR_ADD_FILE_TO_SLATE: <SidebarAddFileToSlate />,
SIDEBAR_CREATE_SLATE: <SidebarCreateSlate />,
SIDEBAR_DRAG_DROP_NOTICE: <SidebarDragDropNotice />,
SIDEBAR_SINGLE_SLATE_SETTINGS: <SidebarSingleSlateSettings />,
@ -104,6 +106,7 @@ export default class ApplicationPage extends React.Component {
sidebar: null,
sidebarLoading: false,
online: null,
sidebar: <SidebarAddFileToSlate />, //remove this
};
async componentDidMount() {
@ -743,6 +746,7 @@ export default class ApplicationPage extends React.Component {
selected: this.state.selected,
viewer: this.state.viewer,
data: this.state.data,
sidebarData: this.state.sidebarData,
fileLoading: this.state.fileLoading,
sidebarLoading: this.state.sidebarLoading,
onSelectedChange: this._handleSelectedChange,

View File

@ -164,7 +164,7 @@ export default class ApplicationUserControls extends React.Component {
<Boundary
captureResize={true}
captureScroll={false}
enabled={this.state.visible}
enabled
onOutsideRectEvent={this._handleHide}
style={this.props.style}
>

View File

@ -10,7 +10,8 @@ const MAX_IN_BYTES = 10737418240 * 4;
const STYLES_CONTAINER = css`
border-radius: 4px;
border: 1px solid ${Constants.system.border};
box-shadow: 0 0 0 1px rgba(229, 229, 229, 0.75) inset,
0 0 40px 0 ${Constants.system.shadow};
padding: 32px;
max-width: 100%;
width: 100%;

View File

@ -14,7 +14,10 @@ import { generateLayout } from "~/components/core/Slate";
import { CheckBox } from "~/components/system/components/CheckBox";
import { Table } from "~/components/core/Table";
import { FileTypeIcon } from "~/components/core/FileTypeIcon";
import { ButtonWarning } from "~/components/system/components/Buttons";
import {
ButtonPrimary,
ButtonWarning,
} from "~/components/system/components/Buttons";
import { TabGroup } from "~/components/core/TabGroup";
import SlateMediaObject from "~/components/core/SlateMediaObject";
@ -40,11 +43,22 @@ const STYLES_ICON_BOX = css`
margin-left: 16px;
`;
const STYLES_CANCEL_BOX = css`
height: 16px;
width: 16px;
background-color: ${Constants.system.brand};
border-radius: 3px;
position: relative;
right: 3px;
cursor: pointer;
box-shadow: 0 0 0 1px ${Constants.system.brand};
`;
const STYLES_HEADER_LINE = css`
display: flex;
align-items: center;
margin-top: 80px;
margin-bottom: 42px;
margin-bottom: 30px;
`;
const STYLES_LINK = css`
@ -77,7 +91,25 @@ const STYLES_ICON_BOX_HOVER = css`
}
`;
const STYLES_ACTION_ROW = css`
const STYLES_ARROWS = css`
display: flex;
align-items: center;
justify-content: flex-end;
`;
const STYLES_ACTION_BAR = css`
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 0 0 1px rgba(229, 229, 229, 0.75) inset,
0 0 40px 0 ${Constants.system.shadow};
border-radius: 4px;
padding: 12px 32px;
background-color: rgba(248, 248, 248, 0.75);
`;
const STYLES_RIGHT = css`
flex-shrink: 0;
display: flex;
align-items: center;
`;
@ -87,8 +119,8 @@ const STYLES_LEFT = css`
min-width: 10%;
`;
const STYLES_RIGHT = css`
flex-shrink: 0;
const STYLES_FILES_SELECTED = css`
font-family: ${Constants.font.semiBold};
`;
const STYLES_ICON_ELEMENT = css`
@ -120,10 +152,8 @@ const STYLES_COPY_INPUT = css`
`;
const STYLES_IMAGE_GRID = css`
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(214px, 1fr));
margin: 0 -27px;
`;
@ -134,7 +164,8 @@ const STYLES_IMAGE_BOX = css`
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0px 0px 0px 1px rgba(229, 229, 229, 0.5) inset;
box-shadow: 0px 0px 0px 1px rgba(229, 229, 229, 0.75) inset,
0 0 40px 0 ${Constants.system.shadow};
cursor: pointer;
`;
@ -165,6 +196,7 @@ export default class DataView extends React.Component {
"remote-slate-object-add",
this._handleRemoteSlateObjectAdd
);
window.addEventListener("remote-update-carousel", this._handleUpdate);
}
await this._handleUpdate();
@ -185,6 +217,7 @@ export default class DataView extends React.Component {
"remote-slate-object-add",
this._handleRemoteSlateObjectAdd
);
window.removeEventListener("remote-update-carousel", this._handleUpdate);
}
_increment = (direction) => {
@ -218,9 +251,8 @@ export default class DataView extends React.Component {
if (!window.confirm(message)) {
return;
}
console.log(this.state.checked);
let cids = Object.keys(this.state.checked).map((id) => {
let index = parseInt(id.replace("checkbox-", ""));
let index = parseInt(id);
return this.props.viewer.library[0].children[index].ipfs.replace(
"/ipfs/",
""
@ -372,13 +404,9 @@ export default class DataView extends React.Component {
detail: { loading: { id: slate.id } },
});
const addResponse = await fetch(`/api/slates/add-url`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ slate, data: [{ title: data.name, ...data }] }),
const addResponse = await Actions.addFileToSlate({
slate,
data: [{ title: data.name, ...data }],
});
if (!addResponse) {
@ -491,10 +519,6 @@ export default class DataView extends React.Component {
this.setState({ menu: null });
};
_handleRemoteDeletion = async (e) => {
await this._handleDelete(e.detail.cid);
};
_handleLoading = ({ cids }) => {
let loading = this.state.loading;
for (let cid of cids) {
@ -511,6 +535,23 @@ export default class DataView extends React.Component {
this.setState({ [e.target.name]: e.target.value });
};
_handleAddToSlate = (e) => {
let userFiles = this.props.viewer.library[0].children;
let files = Object.keys(this.state.checked).map(
(index) => userFiles[index]
);
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_SLATE",
data: { files },
});
this._handleUncheckAll();
};
_handleUncheckAll = () => {
this.setState({ checked: {} });
};
render() {
const header = (
<div css={STYLES_HEADER_LINE}>
@ -552,25 +593,8 @@ export default class DataView extends React.Component {
</div>
);
const footer = (
<div css={STYLES_ACTION_ROW}>
<div css={STYLES_LEFT}>
{Object.keys(this.state.checked).length ? (
<ButtonWarning
style={{ width: 160 }}
onClick={this._handleDeleteFiles}
loading={
this.state.loading &&
Object.values(this.state.loading).some((elem) => {
return !!elem;
})
}
>
Delete {Object.keys(this.state.checked).length} file
{Object.keys(this.state.checked).length > 1 ? "s" : ""}
</ButtonWarning>
) : null}
</div>
<div css={STYLES_RIGHT}>
<React.Fragment>
<div css={STYLES_ARROWS}>
<span
css={STYLES_ICON_ELEMENT}
style={
@ -604,7 +628,33 @@ export default class DataView extends React.Component {
<SVG.NavigationArrow height="24px" />
</span>
</div>
{Object.keys(this.state.checked).length ? (
<div css={STYLES_ACTION_BAR}>
<div css={STYLES_LEFT}>
<span css={STYLES_FILES_SELECTED}>
{Object.keys(this.state.checked).length} files selected
</span>
</div>
<div css={STYLES_RIGHT}>
<ButtonPrimary transparent onClick={this._handleAddToSlate}>
Add to slate
</ButtonPrimary>
<ButtonWarning
transparent
onClick={this._handleDeleteFiles}
loading={
this.state.loading &&
Object.values(this.state.loading).some((elem) => {
return !!elem;
})
}
>
Delete files
</ButtonWarning>
</div>
</div>
) : null}
</React.Fragment>
);
if (this.state.view === "grid") {
return (
@ -640,7 +690,19 @@ export default class DataView extends React.Component {
const columns = [
{
key: "checkbox",
name: <span />,
name: Object.keys(this.state.checked).length ? (
<div
css={STYLES_CANCEL_BOX}
onClick={() => this.setState({ checked: {} })}
>
<SVG.Minus
height="16px"
style={{ color: Constants.system.white }}
/>
</div>
) : (
<span />
),
width: "24px",
},
{
@ -667,7 +729,7 @@ export default class DataView extends React.Component {
return {
...each,
checkbox: this._handleCheckBox ? (
checkbox: (
<div
style={{
margin: "12px 0",
@ -680,19 +742,13 @@ export default class DataView extends React.Component {
}}
>
<CheckBox
name={`checkbox-${this.state.startIndex + index}`}
value={
!!this.state.checked[
`checkbox-${this.state.startIndex + index}`
]
}
name={this.state.startIndex + index}
value={!!this.state.checked[this.state.startIndex + index]}
onChange={this._handleCheckBox}
boxStyle={{ height: 16, width: 16 }}
style={{ position: "relative", right: 3 }}
/>
</div>
) : (
<div />
),
name: (
<div

View File

@ -8,7 +8,7 @@ import { css } from "@emotion/react";
const STYLES_EMPTY_STATE = css`
width: 100%;
height: 328px;
border: 1px solid ${Constants.system.border};
border: 1px solid rgba(229, 229, 229, 0.75);
display: flex;
align-items: center;
justify-content: center;

View File

@ -37,7 +37,7 @@ const STYLES_DROPDOWN_ITEM = css`
margin-bottom: -1px;
:hover {
border-color: ${Constants.system.border} !important;
border-color: rgba(229, 229, 229, 0.75) !important;
}
`;
@ -205,7 +205,7 @@ export class SearchDropdown extends React.Component {
style={{
borderColor:
this.state.selectedIndex === i
? Constants.system.border
? "rgba(229, 229, 229, 0.75)"
: "transparent",
...this.props.itemStyle,
}}

View File

@ -11,6 +11,22 @@ import { dispatchCustomEvent } from "~/common/custom-events";
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
const STYLES_CREATE_NEW = css`
color: ${Constants.system.darkGray};
box-shadow: 0px 0px 0px 1px rgba(229, 229, 229, 0.5) inset;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 160px;
margin: 0px 12px;
@media (max-width: ${Constants.sizes.mobile}px) {
margin: 0 8px;
}
`;
const STYLES_IMAGE_ROW = css`
display: flex;
flex-direction: row;
@ -18,9 +34,11 @@ const STYLES_IMAGE_ROW = css`
height: 160px;
justify-content: space-between;
overflow: hidden;
margin: 0 -12px;
@media (max-width: ${Constants.sizes.mobile}px) {
justify-content: center;
margin: 0 -8px;
}
`;
@ -35,19 +53,21 @@ const STYLES_ITEM_BOX = css`
cursor: pointer;
@media (max-width: ${Constants.sizes.mobile}px) {
margin: 0 auto;
margin: 0 8px;
}
:hover {
color: ${Constants.system.brand};
}
`;
:first-of-type {
margin-left: 0px;
}
const STYLES_EMPTY_BOX = css`
width: 160px;
height: 160px;
margin: 0px 12px;
:last-of-type {
margin-right: 0px;
@media (max-width: ${Constants.sizes.mobile}px) {
margin: 0 8px;
}
`;
@ -57,6 +77,7 @@ const STYLES_IMAGE_ROW_SMALL = css`
flex-wrap: wrap;
height: 56px;
overflow: hidden;
margin: 0 -8px;
`;
const STYLES_ITEM_BOX_SMALL = css`
@ -67,28 +88,30 @@ const STYLES_ITEM_BOX_SMALL = css`
align-items: center;
justify-content: center;
box-shadow: 0px 0px 0px 1px rgba(229, 229, 229, 0.5) inset;
`;
:first-of-type {
margin-left: 0px;
}
:last-of-type {
margin-right: 0px;
}
const STYLES_EMPTY_BOX_SMALL = css`
width: 56px;
height: 56px;
margin: 0px 8px;
`;
export function SlatePreviewRow(props) {
let numItems = props.numItems || 5;
let objects =
let objects;
if (props.slate.data.objects.length === 0) {
objects = [
<div css={STYLES_CREATE_NEW} key="add-files">
<SVG.Plus height="24px" />
<div>Add Files</div>
</div>,
];
} else {
let trimmed =
props.slate.data.objects.length > numItems
? props.slate.data.objects.slice(0, numItems)
: props.slate.data.objects;
return (
<div
css={props.small ? STYLES_IMAGE_ROW_SMALL : STYLES_IMAGE_ROW}
style={props.containerStyle}
>
{objects.map((each) => (
objects = trimmed.map((each) => (
<div
key={each.id}
css={props.small ? STYLES_ITEM_BOX_SMALL : STYLES_ITEM_BOX}
@ -103,13 +126,34 @@ export function SlatePreviewRow(props) {
small={props.small}
/>
</div>
))}
));
}
let numExtra = props.numItems
? props.numItems - objects.length
: 5 - objects.length;
let extra = [];
for (let i = 0; i < numExtra; i++) {
extra.push(
<div
key={`extra-${i}`}
css={props.small ? STYLES_EMPTY_BOX_SMALL : STYLES_EMPTY_BOX}
/>
);
}
return (
<div
css={props.small ? STYLES_IMAGE_ROW_SMALL : STYLES_IMAGE_ROW}
style={props.containerStyle}
>
{objects}
{extra}
</div>
);
}
const STYLES_BLOCK = css`
box-shadow: 0 0 0 1px ${Constants.system.border} inset;
box-shadow: 0 0 0 1px rgba(229, 229, 229, 0.75) inset,
0 0 40px 0 ${Constants.system.shadow};
border-radius: 8px;
padding: 32px 40px;
font-size: 12px;
@ -153,17 +197,6 @@ const STYLES_BODY = css`
word-wrap: break-word;
`;
const STYLES_CREATE_NEW = css`
color: ${Constants.system.darkGray};
box-shadow: 0px 0px 0px 1px rgba(229, 229, 229, 0.5) inset;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 160px;
`;
const STYLES_ICON_BOX = css`
height: 32px;
width: 32px;
@ -354,20 +387,10 @@ export default class SlatePreviewBlock extends React.Component {
) : (
<div style={{ height: "8px" }} />
)}
{this.props.slate.data.objects &&
this.props.slate.data.objects.length ? (
<SlatePreviewRow
{...this.props}
previewStyle={this.props.previewStyle}
/>
) : (
<div css={STYLES_IMAGE_ROW}>
<div css={STYLES_CREATE_NEW}>
<SVG.Plus height="24px" />
<div>Add Files</div>
</div>
</div>
)}
</div>
);
}

View File

@ -20,6 +20,7 @@ const TABLE_COLUMN_WIDTH_DEFAULTS = {
const STYLES_CONTAINER = css`
border: 1px solid rgba(229, 229, 229, 0.75);
box-shadow: 0 0 40px 0 ${Constants.system.shadow};
`;
const STYLES_TABLE_ROW = css`

View File

@ -0,0 +1,180 @@
import * as React from "react";
import * as Strings from "~/common/strings";
import * as Constants from "~/common/constants";
import * as System from "~/components/system";
import * as Validations from "~/common/validations";
import * as SVG from "~/common/svg";
import * as Actions from "~/common/actions";
import { dispatchCustomEvent } from "~/common/custom-events";
import { css } from "@emotion/react";
import { ButtonPrimary } from "~/components/system/components/Buttons";
const STYLES_SLATE_NAME = css`
overflow: hidden;
text-overflow: ellipsis;
font-family: ${Constants.font.medium};
`;
const STYLES_HEADER = css`
font-family: ${Constants.font.semiBold};
font-size: 18px;
margin-top: 32px;
margin-bottom: 16px;
`;
const STYLES_SLATE_LIST = css`
max-height: 400px;
overflow-y: scroll;
`;
const STYLES_SLATE_LINE = css`
display: flex;
align-items: center;
width: 100%;
padding: 12px 16px;
background-color: ${Constants.system.white};
margin-bottom: 1px;
cursor: pointer;
`;
const STYLES_ICON_BOX = css`
display: flex;
align-items: center;
`;
export default class SidebarAddFileToSlate extends React.Component {
state = {
selected: {},
};
_handleCreateSlate = async () => {
if (
Object.values(this.state.selected).some((value) => {
return !!value;
})
) {
await this._handleSubmit();
}
await this.props.onCancel();
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_CREATE_SLATE",
data: this.props.sidebarData,
});
};
_handleAdd = (slate) => {
if (this.state.selected[slate.id]) {
this.setState({
selected: { ...this.state.selected, [slate.id]: false },
});
} else {
this.setState({
selected: { ...this.state.selected, [slate.id]: slate },
});
}
};
_handleSubmit = async () => {
let data = this.props.sidebarData.files.map((file) => {
return { title: file.name, ...file };
});
for (let slate of Object.values(this.state.selected)) {
if (!slate) continue;
const addResponse = await Actions.addFileToSlate({ slate, data });
if (!addResponse) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
message:
"We're having trouble connecting right now. Please try again later",
},
},
});
return;
} else if (addResponse.error) {
dispatchCustomEvent({
name: "create-alert",
detail: { alert: { decorator: addResponse.decorator } },
});
return;
}
}
await this.props.onRehydrate();
dispatchCustomEvent({
name: "remote-update-carousel",
detail: null,
});
this.props.onCancel();
};
render() {
return (
<div>
<System.P
style={{
fontFamily: Constants.font.semiBold,
fontSize: Constants.typescale.lvl3,
marginBottom: "64px",
}}
>
Add files to slate
</System.P>
<System.P css={STYLES_HEADER}>Slates</System.P>
<div
css={STYLES_SLATE_LINE}
style={{ marginBottom: 32 }}
onClick={this._handleCreateSlate}
>
<SVG.Plus
height="24px"
style={{ color: Constants.system.brand, marginRight: 8 }}
/>
<div
css={STYLES_SLATE_NAME}
style={{ color: Constants.system.brand }}
>
Create new slate
</div>
</div>
<div css={STYLES_SLATE_LIST}>
{this.props.viewer.slates.map((slate) => (
<div css={STYLES_SLATE_LINE} onClick={() => this._handleAdd(slate)}>
<div css={STYLES_ICON_BOX}>
{this.state.selected[slate.id] ? (
<SVG.Slate height="24px" style={{ marginRight: 8 }} />
) : (
<SVG.PlusCircle
height="24px"
style={{ color: Constants.system.darkGray, marginRight: 8 }}
/>
)}
</div>
<div
css={STYLES_SLATE_NAME}
style={
this.state.selected[slate.id]
? null
: { color: Constants.system.darkGray }
}
>
{slate.data.name || slate.slatename}
</div>
</div>
))}
</div>
<ButtonPrimary
full
onClick={this._handleSubmit}
style={{ marginTop: 32 }}
>
Add to slates
</ButtonPrimary>
</div>
);
}
}

View File

@ -3,6 +3,7 @@ import * as Strings from "~/common/strings";
import * as Constants from "~/common/constants";
import * as System from "~/components/system";
import * as Validations from "~/common/validations";
import * as Actions from "~/common/actions";
import { dispatchCustomEvent } from "~/common/custom-events";
import { css } from "@emotion/react";
@ -87,6 +88,48 @@ export default class SidebarCreateSlate extends React.Component {
return;
}
if (
this.props.sidebarData &&
this.props.sidebarData.files &&
this.props.sidebarData.files[0].decorator === "FILE"
) {
let data = this.props.sidebarData.files.map((file) => {
return { title: file.name, ...file };
});
const addResponse = await Actions.addFileToSlate({
slate: response.slate,
data,
});
if (!addResponse) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
message:
"We're having trouble connecting right now. Please try again later",
},
},
});
return;
}
if (addResponse.error) {
dispatchCustomEvent({
name: "create-alert",
detail: { alert: { decorator: response.decorator } },
});
return;
}
await this.props.onRehydrate();
dispatchCustomEvent({
name: "remote-update-carousel",
detail: null,
});
}
this.setState({ loading: false });
this.props.onAction({
type: "NAVIGATE",

View File

@ -28,7 +28,7 @@ export default class SidebarCreateSlate extends React.Component {
_handleSubmit = async () => {
this.setState({ loading: true });
if (!this.state.email || !this.state.email.length) {
if (Strings.isEmpty(this.state.email)) {
dispatchCustomEvent({
name: "create-alert",
detail: {
@ -54,17 +54,6 @@ export default class SidebarCreateSlate extends React.Component {
return;
}
if (!Validations.email(this.state.email)) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: { message: "Please check that your email address is valid" },
},
});
this.setState({ loading: false });
return;
}
const response = await Actions.createSupportMessage({
username: this.props.viewer.username,
name: this.state.name,

View File

@ -11,32 +11,13 @@ const STYLES_BUTTON = `
outline: 0;
border: 0;
min-height: 40px;
padding: 6px 24px 6px 24px;
padding: 4px 16px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-size: 14px;
letter-spacing: 0.2px;
font-family: ${Constants.font.semiBold};
transition: 200ms ease all;
overflow-wrap: break-word;
user-select: none;
`;
const STYLES_BUTTON_FULL = `
box-sizing: border-box;
border-radius: 4px;
outline: 0;
border: 0;
min-height: 40px;
padding: 6px 24px 6px 24px;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
font-size: 12px;
letter-spacing: 0.2px;
font-family: ${Constants.font.semiBold};
font-family: ${Constants.font.medium};
transition: 200ms ease all;
overflow-wrap: break-word;
user-select: none;
@ -60,30 +41,25 @@ const STYLES_BUTTON_PRIMARY = css`
}
`;
const STYLES_BUTTON_PRIMARY_FULL = css`
${STYLES_BUTTON_FULL}
const STYLES_BUTTON_PRIMARY_TRANSPARENT = css`
${STYLES_BUTTON}
${"" /* font-size: 16px;
font-family: ${Constants.font.medium}; */}
cursor: pointer;
background-color: ${Constants.system.brand};
color: ${Constants.system.white};
:hover {
background-color: #065ca8;
}
:focus {
box-shadow: inset 0 0 5px 2px rgba(0, 0, 0, 0.3);
background-color: #065ca8;
outline: 0;
border: 0;
}
background-color: transparent;
color: ${Constants.system.brand};
`;
export const ButtonPrimary = (props) => {
if (props.loading) {
return (
<button
css={props.full ? STYLES_BUTTON_PRIMARY_FULL : STYLES_BUTTON_PRIMARY}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_PRIMARY_TRANSPARENT
: STYLES_BUTTON_PRIMARY
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
>
<LoaderSpinner style={{ height: 16, width: 16 }} />
</button>
@ -93,8 +69,12 @@ export const ButtonPrimary = (props) => {
if (props.type === "label") {
return (
<label
css={props.full ? STYLES_BUTTON_PRIMARY_FULL : STYLES_BUTTON_PRIMARY}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_PRIMARY_TRANSPARENT
: STYLES_BUTTON_PRIMARY
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
children={props.children}
type={props.label}
htmlFor={props.htmlFor}
@ -106,8 +86,12 @@ export const ButtonPrimary = (props) => {
return (
<button
css={props.full ? STYLES_BUTTON_PRIMARY_FULL : STYLES_BUTTON_PRIMARY}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_PRIMARY_TRANSPARENT
: STYLES_BUTTON_PRIMARY
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
onMouseUp={props.onClick}
onTouchEnd={props.onClick}
children={props.children}
@ -137,22 +121,11 @@ const STYLES_BUTTON_SECONDARY = css`
}
`;
const STYLES_BUTTON_SECONDARY_FULL = css`
${STYLES_BUTTON_FULL}
const STYLES_BUTTON_SECONDARY_TRANSPARENT = css`
${STYLES_BUTTON}
cursor: pointer;
background-color: ${Constants.system.white};
box-shadow: 0 0 0 1px ${Constants.system.border} inset;
color: ${Constants.system.brand};
:hover {
${"" /* box-shadow: 0 0 0 1px #065ca8 inset;
color: #065ca8; */}
}
:focus {
outline: 0;
border: 0;
}
background-color: transparent;
color: ${Constants.system.darkGray};
`;
export const ButtonSecondary = (props) => {
@ -160,9 +133,11 @@ export const ButtonSecondary = (props) => {
return (
<button
css={
props.full ? STYLES_BUTTON_SECONDARY_FULL : STYLES_BUTTON_SECONDARY
props.transparent
? STYLES_BUTTON_SECONDARY_TRANSPARENT
: STYLES_BUTTON_SECONDARY
}
style={props.style}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
>
<LoaderSpinner style={{ height: 16, width: 16 }} />
</button>
@ -173,9 +148,11 @@ export const ButtonSecondary = (props) => {
return (
<label
css={
props.full ? STYLES_BUTTON_SECONDARY_FULL : STYLES_BUTTON_SECONDARY
props.transparent
? STYLES_BUTTON_SECONDARY_TRANSPARENT
: STYLES_BUTTON_SECONDARY
}
style={props.style}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
onMouseUp={props.onClick}
onTouchEnd={props.onClick}
children={props.children}
@ -187,8 +164,13 @@ export const ButtonSecondary = (props) => {
return (
<button
css={props.full ? STYLES_BUTTON_SECONDARY_FULL : STYLES_BUTTON_SECONDARY}
css={
props.transparent
? STYLES_BUTTON_SECONDARY_TRANSPARENT
: STYLES_BUTTON_SECONDARY
}
{...props}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
/>
);
};
@ -209,23 +191,23 @@ const STYLES_BUTTON_DISABLED = css`
}
`;
const STYLES_BUTTON_DISABLED_FULL = css`
${STYLES_BUTTON_FULL}
const STYLES_BUTTON_DISABLED_TRANSPARENT = css`
${STYLES_BUTTON}
cursor: not-allowed;
background-color: ${Constants.system.gray};
color: ${Constants.system.darkGray};
:focus {
outline: 0;
border: 0;
}
background-color: transparent;
color: ${Constants.system.gray};
`;
export const ButtonDisabled = (props) => {
return (
<button
css={props.full ? STYLES_BUTTON_DISABLED_FULL : STYLES_BUTTON_DISABLED}
css={
props.transparent
? STYLES_BUTTON_DISABLED_TRANSPARENT
: STYLES_BUTTON_DISABLED
}
{...props}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
/>
);
};
@ -252,30 +234,23 @@ const STYLES_BUTTON_WARNING = css`
}
`;
const STYLES_BUTTON_WARNING_FULL = css`
${STYLES_BUTTON_FULL}
const STYLES_BUTTON_WARNING_TRANSPARENT = css`
${STYLES_BUTTON}
cursor: pointer;
background-color: #e0e0e0;
background-color: transparent;
color: ${Constants.system.red};
:hover {
background-color: #d4d4d4;
}
:focus {
box-shadow: inset 0 0 5px 2px rgba(0, 0, 0, 0.3);
background-color: #d4d4d4;
outline: 0;
border: 0;
}
`;
export const ButtonWarning = (props) => {
if (props.loading) {
return (
<button
css={props.full ? STYLES_BUTTON_WARNING_FULL : STYLES_BUTTON_WARNING}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_WARNING_TRANSPARENT
: STYLES_BUTTON_WARNING
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
>
<LoaderSpinner style={{ height: 16, width: 16 }} />
</button>
@ -285,8 +260,12 @@ export const ButtonWarning = (props) => {
if (props.type === "label") {
return (
<label
css={props.full ? STYLES_BUTTON_WARNING_FULL : STYLES_BUTTON_WARNING}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_WARNING_TRANSPARENT
: STYLES_BUTTON_WARNING
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
children={props.children}
type={props.label}
htmlFor={props.htmlFor}
@ -298,8 +277,12 @@ export const ButtonWarning = (props) => {
return (
<button
css={props.full ? STYLES_BUTTON_WARNING_FULL : STYLES_BUTTON_WARNING}
style={props.style}
css={
props.transparent
? STYLES_BUTTON_WARNING_TRANSPARENT
: STYLES_BUTTON_WARNING
}
style={{ ...props.style, width: props.full ? "100%" : "auto" }}
onMouseUp={props.onClick}
onTouchEnd={props.onClick}
children={props.children}

View File

@ -74,8 +74,27 @@ export class CheckBox extends React.Component {
render() {
return (
<label css={STYLES_CHECKBOX} style={this.props.style}>
<figure css={STYLES_CHECKBOX_FIGURE} style={this.props.boxStyle}>
{this.props.value ? <SVG.CheckBox height="20px" /> : null}
<figure
css={STYLES_CHECKBOX_FIGURE}
style={
this.props.value
? {
backgroundColor: Constants.system.brand,
boxShadow: `0 0 0 1px ${Constants.system.brand}`,
...this.props.boxStyle,
}
: {
backgroundColor: Constants.system.white,
...this.props.boxStyle,
}
}
>
{this.props.value ? (
<SVG.CheckBox
height="14px"
style={{ color: Constants.system.white }}
/>
) : null}
</figure>
<input
css={STYLES_CHECKBOX_INPUT}

View File

@ -70,6 +70,11 @@ export default async (req, res) => {
}
let newObjects = [];
if (Array.isArray(req.body.data)) {
newObjects = [...req.body.data];
} else {
newObjects = [req.body.data];
}
const isArray = req.body.data && req.body.data.length;
if (isArray) {
newObjects = [...req.body.data];

View File

@ -8,6 +8,10 @@ import { css } from "@emotion/react";
import { TabGroup } from "~/components/core/TabGroup";
import { Boundary } from "~/components/system/components/fragments/Boundary";
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
import {
ButtonPrimary,
ButtonSecondary,
} from "~/components/system/components/Buttons";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
@ -178,21 +182,22 @@ export default class SceneDirectory extends React.Component {
.map((relation) => {
let button = (
<div css={STYLES_BUTTONS}>
<div
css={STYLES_ACTION_BUTTON}
<ButtonPrimary
transparent
style={{ fontSize: 16 }}
onClick={(e) => this._handleAccept(e, relation.owner.id)}
>
Accept
</div>
<div
css={STYLES_ACTION_BUTTON}
style={{ color: Constants.system.darkGray, marginLeft: "16px" }}
</ButtonPrimary>
<ButtonSecondary
transparent
style={{ fontSize: 16 }}
onClick={(e) => {
this._handleDelete(e, relation.id);
}}
>
Decline
</div>
</ButtonSecondary>
</div>
);
return (

View File

@ -50,6 +50,7 @@ export default class SceneFilesFolder extends React.Component {
{this.props.viewer.library[0].children &&
this.props.viewer.library[0].children.length ? (
<DataView
onAction={this.props.onAction}
viewer={this.props.viewer}
items={this.props.viewer.library[0].children}
onRehydrate={this.props.onRehydrate}

View File

@ -54,18 +54,6 @@ export default class SceneHome extends React.Component {
{hasChildren ? (
<div style={{ marginTop: "48px" }}>
<DataView
buttons={[
{
name: "View files",
type: "NAVIGATE",
value: this.props.viewer.library[0].id,
},
{
name: "Upload data",
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
},
]}
viewer={this.props.viewer}
items={this.props.viewer.library[0].children}
onAction={this.props.onAction}

View File

@ -516,6 +516,7 @@ export default class SceneSlate extends React.Component {
<div onClick={this._handleFollow}>
{following ? (
<ButtonSecondary
transparent
style={{ minWidth: 120 }}
loading={this.state.followLoading}
>
@ -523,6 +524,7 @@ export default class SceneSlate extends React.Component {
</ButtonSecondary>
) : (
<ButtonPrimary
transparent
style={{ minWidth: 120 }}
loading={this.state.followLoading}
>