"axios": "0.18.0",
"classnames": "2.2.6",
"lodash": "4.17.10",
- "react": "16.4.2",
+ "react": "16.5.2",
"react-copy-to-clipboard": "5.0.1",
- "react-dom": "16.4.2",
+ "react-dom": "16.5.2",
"react-dropzone": "5.0.1",
"react-redux": "5.0.7",
"react-router": "4.3.1",
"react-router-dom": "4.3.1",
"react-router-redux": "5.0.0-alpha.9",
+ "react-rte": "0.16.1",
"react-scripts-ts": "2.17.0",
"react-splitter-layout": "3.0.1",
"react-transition-group": "2.4.0",
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { withStyles, StyleRulesCallback, WithStyles, Typography } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { openRichTextEditorDialog } from '~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions';
+
+type CssRules = "root";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ root: {
+ color: theme.palette.primary.main,
+ cursor: 'pointer'
+ }
+});
+
+interface RichTextEditorLinkData {
+ title: string;
+ label: string;
+ content: string;
+}
+
+interface RichTextEditorLinkActions {
+ onClick: (title: string, content: string) => void;
+}
+
+type RichTextEditorLinkProps = RichTextEditorLinkData & RichTextEditorLinkActions & WithStyles<CssRules>;
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+ onClick: (title: string, content: string) => dispatch<any>(openRichTextEditorDialog(title, content))
+});
+
+export const RichTextEditorLink = connect(undefined, mapDispatchToProps)(
+ withStyles(styles)(({ classes, title, content, label, onClick }: RichTextEditorLinkProps) =>
+ <Typography component='span' className={classes.root}
+ onClick={() => onClick(title, content) }>
+ {label}
+ </Typography>
+ ));
\ No newline at end of file
import { WrappedFieldProps } from 'redux-form';
import { ArvadosTheme } from '~/common/custom-theme';
import { TextField as MaterialTextField, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import RichTextEditor from 'react-rte';
type CssRules = 'textField';
fullWidth={true}
{...props.input}
/>);
+
+
+interface RichEditorTextFieldData {
+ label?: string;
+}
+
+type RichEditorTextFieldProps = RichEditorTextFieldData & WrappedFieldProps & WithStyles<CssRules>;
+
+export const RichEditorTextField = withStyles(styles)(
+ class RichEditorTextField extends React.Component<RichEditorTextFieldProps> {
+ state = {
+ value: RichTextEditor.createValueFromString(this.props.input.value, 'html')
+ };
+
+ onChange = (value: any) => {
+ this.setState({ value });
+ this.props.input.onChange(value.toString('html'));
+ }
+
+ render() {
+ return <RichTextEditor
+ value={this.state.value}
+ onChange={this.onChange}
+ placeholder={this.props.label} />;
+ }
+ }
+);
\ No newline at end of file
import { ServiceRepository } from "~/services/services";
import { ProjectResource } from '~/models/project';
import { ContextMenuResource } from "~/store/context-menu/context-menu-actions";
+import { getResource } from '~/store/resources/resources';
export interface ProjectUpdateFormDialogData {
uuid: string;
export const PROJECT_UPDATE_FORM_NAME = 'projectUpdateFormName';
export const openProjectUpdateDialog = (resource: ContextMenuResource) =>
- (dispatch: Dispatch) => {
- dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const project = getResource(resource.uuid)(getState().resources);
+ dispatch(initialize(PROJECT_UPDATE_FORM_NAME, project));
dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {} }));
};
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+
+export const RICH_TEXT_EDITOR_DIALOG_NAME = 'richTextEditorDialogName';
+export const openRichTextEditorDialog = (title: string, text: string) =>
+ dialogActions.OPEN_DIALOG({ id: RICH_TEXT_EDITOR_DIALOG_NAME, data: { title, text } });
\ No newline at end of file
export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
export const PROJECT_NAME_VALIDATION = [require, maxLength(255)];
-export const PROJECT_DESCRIPTION_VALIDATION = [maxLength(255)];
export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
import { resourceLabel } from '~/common/labels';
import { DetailsData } from "./details-data";
import { DetailsAttribute } from "~/components/details-attribute/details-attribute";
+import { RichTextEditorLink } from '~/components/rich-text-editor-link/rich-text-editor-link';
export class ProjectDetails extends DetailsData<ProjectResource> {
<DetailsAttribute label='Created at' value={formatDate(this.item.createdAt)} />
{/* Missing attr */}
<DetailsAttribute label='File size' value='1.4 GB' />
- <DetailsAttribute label='Description' value={this.item.description} />
+ <DetailsAttribute label='Description'>
+ {this.item.description ?
+ <RichTextEditorLink
+ title={`Description of ${this.item.name}`}
+ content={this.item.description}
+ label='Show full description' />
+ : '---'
+ }
+ </DetailsAttribute>
</div>;
}
}
import * as React from "react";
import { Field } from "redux-form";
-import { TextField } from "~/components/text-field/text-field";
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from "~/validators/validators";
+import { TextField, RichEditorTextField } from "~/components/text-field/text-field";
+import { PROJECT_NAME_VALIDATION } from "~/validators/validators";
export const ProjectNameField = () =>
<Field
export const ProjectDescriptionField = () =>
<Field
name='description'
- component={TextField}
- validate={PROJECT_DESCRIPTION_VALIDATION}
+ component={RichEditorTextField}
label="Description - optional" />;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, DialogContentText } from "@material-ui/core";
+import { WithDialogProps } from "../../store/dialog/with-dialog";
+import { withDialog } from '~/store/dialog/with-dialog';
+import { RICH_TEXT_EDITOR_DIALOG_NAME } from "~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions";
+import RichTextEditor from 'react-rte';
+
+export interface RichTextEditorDialogDataProps {
+ title: string;
+ text: string;
+}
+
+export const RichTextEditorDialog = withDialog(RICH_TEXT_EDITOR_DIALOG_NAME)(
+ (props: WithDialogProps<RichTextEditorDialogDataProps>) =>
+ <Dialog open={props.open}
+ onClose={props.closeDialog}
+ fullWidth
+ maxWidth='sm'>
+ <DialogTitle>{props.data.title}</DialogTitle>
+ <DialogContent>
+ <RichTextEditor
+ value={RichTextEditor.createValueFromString(props.data.text, 'html')}
+ readOnly={true} />
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ onClick={props.closeDialog}>
+ Close
+ </Button>
+ </DialogActions>
+ </Dialog>
+);
\ No newline at end of file
import { ContextMenu } from "~/views-components/context-menu/context-menu";
import { FavoritePanel } from "../favorite-panel/favorite-panel";
import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
+import { RichTextEditorDialog } from '~/views-components/rich-text-editor-dialog/rich-text-editor-dialog';
import { Snackbar } from '~/views-components/snackbar/snackbar';
import { CollectionPanel } from '../collection-panel/collection-panel';
import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
<PartialCopyCollectionDialog />
<ProcessCommandDialog />
<RenameFileDialog />
+ <RichTextEditorDialog />
<Snackbar />
<UpdateCollectionDialog />
<UpdateProcessDialog />
}
declare var System: System;
-declare module 'react-splitter-layout';
\ No newline at end of file
+declare module 'react-splitter-layout';
+declare module 'react-rte';
\ No newline at end of file
mkdirp "^0.5.1"
source-map-support "^0.4.15"
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
dependencies:
chalk "^1.1.3"
+class-autobind@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/class-autobind/-/class-autobind-0.1.4.tgz#34516c49167cf8d3f639ddc186bcfa2268afff34"
+
class-utils@^0.3.5:
version "0.3.6"
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
version "4.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
+draft-js-export-html@>=0.6.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/draft-js-export-html/-/draft-js-export-html-1.2.0.tgz#1cbe2b78e1fed74fc29c7cdcbfd7540468eca209"
+ dependencies:
+ draft-js-utils "^1.2.0"
+
+draft-js-export-markdown@>=0.3.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/draft-js-export-markdown/-/draft-js-export-markdown-1.2.0.tgz#c423d67389e1c70ddd13e956afe82fd1b72feea5"
+ dependencies:
+ draft-js-utils "^1.2.0"
+
+draft-js-import-element@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/draft-js-import-element/-/draft-js-import-element-1.2.1.tgz#9a6a56d74690d48d35d8d089564e6d710b4926eb"
+ dependencies:
+ draft-js-utils "^1.2.0"
+ synthetic-dom "^1.2.0"
+
+draft-js-import-html@>=0.4.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/draft-js-import-html/-/draft-js-import-html-1.2.1.tgz#88adb8ce5dbe1a5a777663b1893cee6a35239eaa"
+ dependencies:
+ draft-js-import-element "^1.2.1"
+
+draft-js-import-markdown@>=0.3.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/draft-js-import-markdown/-/draft-js-import-markdown-1.2.1.tgz#ec18eb15008bab13d9878d65db50e181dd64a5ce"
+ dependencies:
+ draft-js-import-element "^1.2.1"
+ synthetic-dom "^1.2.0"
+
+draft-js-utils@>=0.2.0, draft-js-utils@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/draft-js-utils/-/draft-js-utils-1.2.0.tgz#f5cb23eb167325ffed3d79882fdc317721d2fd12"
+
+draft-js@>=0.10.0:
+ version "0.10.5"
+ resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
+ dependencies:
+ fbjs "^0.8.15"
+ immutable "~3.7.4"
+ object-assign "^4.1.0"
+
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
dependencies:
bser "^2.0.0"
-fbjs@^0.8.1, fbjs@^0.8.16:
+fbjs@^0.8.1, fbjs@^0.8.15, fbjs@^0.8.16:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
dependencies:
minimatch "^3.0.4"
+immutable@^3.8.1:
+ version "3.8.2"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
+
+immutable@~3.7.4:
+ version "3.7.6"
+ resolved "http://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
+
import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
strip-ansi "3.0.1"
text-table "0.2.0"
-react-dom@16.4.2:
- version "16.4.2"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4"
+react-dom@16.5.2:
+ version "16.5.2"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
dependencies:
- fbjs "^0.8.16"
loose-envify "^1.1.0"
object-assign "^4.1.1"
- prop-types "^15.6.0"
+ prop-types "^15.6.2"
+ schedule "^0.5.0"
react-dropzone@5.0.1:
version "5.0.1"
prop-types "^15.6.1"
warning "^4.0.1"
+react-rte@0.16.1:
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/react-rte/-/react-rte-0.16.1.tgz#e345664c87e992d15ec053b406f51ffac6e86622"
+ dependencies:
+ babel-runtime "^6.23.0"
+ class-autobind "^0.1.4"
+ classnames "^2.2.5"
+ draft-js ">=0.10.0"
+ draft-js-export-html ">=0.6.0"
+ draft-js-export-markdown ">=0.3.0"
+ draft-js-import-html ">=0.4.0"
+ draft-js-import-markdown ">=0.3.0"
+ draft-js-utils ">=0.2.0"
+ immutable "^3.8.1"
+
react-scripts-ts@2.17.0:
version "2.17.0"
resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.17.0.tgz#398bae19a30c9b39b3dfe0720ebb40c60c2f6574"
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
-react@16.4.2:
- version "16.4.2"
- resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"
+react@16.5.2:
+ version "16.5.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
dependencies:
- fbjs "^0.8.16"
loose-envify "^1.1.0"
object-assign "^4.1.1"
- prop-types "^15.6.0"
+ prop-types "^15.6.2"
+ schedule "^0.5.0"
read-pkg-up@^1.0.1:
version "1.0.1"
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+schedule@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8"
+ dependencies:
+ object-assign "^4.1.1"
+
schema-utils@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
+synthetic-dom@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/synthetic-dom/-/synthetic-dom-1.2.0.tgz#f3589aafe2b5e299f337bb32973a9be42dd5625e"
+
tapable@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"