Rework text field styling and some layouts

* Change the default text fields to use filled version
* Text fields embedded in InlinePopover or MiniToolbar keep their previous appearance with a single line.
* Add Layout.js with some predefined layouts
* Improved EventsFunctionPropertiesEditor fields
This commit is contained in:
Florian Rival
2019-11-17 21:38:27 +00:00
parent d0247c7df3
commit bd8db5f573
27 changed files with 887 additions and 290 deletions

View File

@@ -41,6 +41,15 @@ const styles = {
};
const validateParameterName = (i18n: I18nType, newName: string) => {
if (!newName) {
showWarningBox(
i18n._(
t`The name of a parameter can not be empty. Enter a name for the parameter or you won't be able to use it.`
)
);
return false;
}
if (!gd.Project.validateObjectName(newName)) {
showWarningBox(
i18n._(
@@ -131,7 +140,7 @@ export default class EventsFunctionParametersEditor extends React.Component<
<SemiControlledTextField
commitOnBlur
margin="none"
hintText={t`Enter the parameter name`}
hintText={t`Enter the parameter name (mandatory)`}
value={parameter.getName()}
onChange={text => {
if (!validateParameterName(i18n, text)) return;
@@ -141,13 +150,6 @@ export default class EventsFunctionParametersEditor extends React.Component<
this.props.onParametersUpdated();
}}
disabled={isParameterDisabled(i)}
errorText={
parameter.getName() ? null : (
<Trans>
Name of the parameter is mandatory.
</Trans>
)
}
fullWidth
/>
</Column>

View File

@@ -1,4 +1,5 @@
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
@@ -13,6 +14,7 @@ import SemiControlledTextField from '../../UI/SemiControlledTextField';
import { isBehaviorLifecycleFunction } from '../../EventsFunctionsExtensionsLoader/MetadataDeclarationHelpers';
import EmptyMessage from '../../UI/EmptyMessage';
import { getParametersIndexOffset } from '../../EventsFunctionsExtensionsLoader';
import { type MessageDescriptor } from '../../Utils/i18n/MessageDescriptor.flow';
const gd = global.gd;
@@ -72,6 +74,30 @@ const getSentenceErrorText = (
return undefined;
};
const getFullNameHintText = (type: any): MessageDescriptor => {
if (type === gd.EventsFunction.Condition) {
return t`Example: Is flashing?`;
} else if (type === gd.EventsFunction.Expression) {
return t`Example: Life remaining`;
} else if (type === gd.EventsFunction.StringExpression) {
return t`Example: Equipped shield name`;
}
return t`Example: Flash the object`;
};
const getDescriptionHintText = (type: any): MessageDescriptor => {
if (type === gd.EventsFunction.Condition) {
return t`Example: Check if the object is flashing.`;
} else if (type === gd.EventsFunction.Expression) {
return t`Example: Life remaining for the player.`;
} else if (type === gd.EventsFunction.StringExpression) {
return t`Example: Name of the shield equipped by the player.`;
}
return t`Example: Make the object flash for 5 seconds.`;
};
export default class EventsFunctionPropertiesEditor extends React.Component<
Props,
State
@@ -109,6 +135,7 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
<Column expand>
<SelectField
value={type}
floatingLabelText={<Trans>Function type</Trans>}
fullWidth
disabled={!!freezeEventsFunctionType}
onChange={(e, i, value: string) => {
@@ -138,7 +165,10 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
<Column expand>
<SemiControlledTextField
commitOnBlur
hintText={t`Full name displayed in editor`}
floatingLabelText={
<Trans>Full name displayed in editor</Trans>
}
hintText={getFullNameHintText(type)}
value={eventsFunction.getFullName()}
onChange={text => {
eventsFunction.setFullName(text);
@@ -152,7 +182,10 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
<Line noMargin>
<SemiControlledTextField
commitOnBlur
hintText={t`Description, displayed in editor`}
floatingLabelText={
<Trans>Description, displayed in editor</Trans>
}
hintText={getDescriptionHintText(type)}
fullWidth
multiLine
value={eventsFunction.getDescription()}
@@ -168,7 +201,8 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
type === gd.EventsFunction.Condition ? (
<SemiControlledTextField
commitOnBlur
hintText={t`Sentence in Events Sheet (write _PARAMx_ for parameters, e.g: _PARAM1_)`}
floatingLabelText={<Trans>Sentence in Events Sheet</Trans>}
hintText={t`Note: write _PARAMx_ for parameters, e.g: Flash _PARAM1_ for 5 seconds`}
fullWidth
value={eventsFunction.getSentence()}
onChange={text => {

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import Popper from '@material-ui/core/Popper';
import Background from '../UI/Background';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import { Column } from '../UI/Grid';
import { Column, Line } from '../UI/Grid';
const styles = {
popover: {
@@ -20,9 +20,6 @@ const styles = {
// then. Only one InlinePopover should be shown at a time anyway.
zIndex: 2,
},
contentContainer: {
overflow: 'hidden',
},
};
export default class InlinePopover extends Component {
@@ -36,8 +33,10 @@ export default class InlinePopover extends Component {
placement="bottom"
>
<Background>
<Column>
<div style={styles.contentContainer}>{this.props.children}</div>
<Column expand>
<Line>
{this.props.children}
</Line>
</Column>
</Background>
</Popper>

View File

@@ -20,18 +20,9 @@ import BackgroundHighlighting, {
import debounce from 'lodash/debounce';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Paper from '@material-ui/core/Paper';
import { TextFieldWithButtonLayout } from '../../../UI/Layout';
const gd = global.gd;
export const textFieldRightButtonMargins = {
marginTop: 17, //Properly align with the text field
marginLeft: 10,
};
export const textFieldWithLabelRightButtonMargins = {
marginTop: 33, //Properly align with the text field
marginLeft: 10,
};
const styles = {
container: {
display: 'flex',
@@ -53,10 +44,14 @@ const styles = {
lineHeight: 1.4,
},
backgroundHighlighting: {
marginTop: 22, //Properly align with the text field
marginTop: 13, //Properly align with the text field
paddingLeft: 12,
paddingRight: 12,
},
backgroundHighlightingWithDescription: {
marginTop: 38, //Properly align with the text field
marginTop: 29, //Properly align with the text field
paddingLeft: 12,
paddingRight: 12,
},
};
@@ -277,80 +272,85 @@ export default class ExpressionField extends React.Component<Props, State> {
: styles.backgroundHighlighting;
return (
<div style={styles.container}>
<div style={styles.textFieldContainer}>
<div style={styles.textFieldAndHightlightContainer}>
<BackgroundHighlighting
value={this.state.validatedValue}
style={{ ...styles.input, ...backgroundHighlightingStyle }}
highlights={this.state.errorHighlights}
/>
<SemiControlledTextField
value={value}
floatingLabelText={description}
hintText={expressionType === 'string' ? '""' : undefined}
inputStyle={styles.input}
onChange={this._handleChange}
onBlur={this._handleBlur}
ref={field => (this._field = field)}
onFocus={this._handleFocus}
errorText={this.state.errorText}
multiLine
fullWidth
/>
</div>
{this._fieldElement && this.state.popoverOpen && (
<ClickAwayListener onClickAway={this._handleRequestClose}>
<Popper
style={popoverStyle}
open={this.state.popoverOpen}
anchorEl={this._fieldElement}
placement="bottom"
disablePortal={
true /* Can't use portals as this would put the Popper outside of the Modal, which is keeping the focus in the modal (so the search bar and keyboard browsing won't not work) */
}
>
<Paper style={styles.expressionSelectorPopoverContent}>
<ExpressionSelector
selectedType=""
onChoose={(type, expression) => {
this._handleExpressionChosen(expression);
}}
expressionType={expressionType}
focusOnMount
scope={scope}
/>
</Paper>
</Popper>
</ClickAwayListener>
<React.Fragment>
<TextFieldWithButtonLayout
margin={this.props.isInline ? 'none' : 'dense'}
renderTextField={() => (
<div style={styles.textFieldContainer}>
<div style={styles.textFieldAndHightlightContainer}>
<BackgroundHighlighting
value={this.state.validatedValue}
style={{ ...styles.input, ...backgroundHighlightingStyle }}
highlights={this.state.errorHighlights}
/>
<SemiControlledTextField
margin={this.props.isInline ? 'none' : 'dense'}
value={value}
floatingLabelText={description}
hintText={expressionType === 'string' ? '""' : undefined}
inputStyle={styles.input}
onChange={this._handleChange}
onBlur={this._handleBlur}
ref={field => (this._field = field)}
onFocus={this._handleFocus}
errorText={this.state.errorText}
multiLine
fullWidth
/>
</div>
{this._fieldElement && this.state.popoverOpen && (
<ClickAwayListener onClickAway={this._handleRequestClose}>
<Popper
style={popoverStyle}
open={this.state.popoverOpen}
anchorEl={this._fieldElement}
placement="bottom"
disablePortal={
true /* Can't use portals as this would put the Popper outside of the Modal, which is keeping the focus in the modal (so the search bar and keyboard browsing won't not work) */
}
>
<Paper style={styles.expressionSelectorPopoverContent}>
<ExpressionSelector
selectedType=""
onChoose={(type, expression) => {
this._handleExpressionChosen(expression);
}}
expressionType={expressionType}
focusOnMount
scope={scope}
/>
</Paper>
</Popper>
</ClickAwayListener>
)}
</div>
)}
</div>
{!this.props.isInline &&
this.props.renderExtraButton &&
this.props.renderExtraButton({
style: description
? textFieldWithLabelRightButtonMargins
: textFieldRightButtonMargins,
})}
{!this.props.isInline && (
<RaisedButton
icon={<Functions />}
label={
expressionType === 'string'
? '"ABC"'
: expressionType === 'number'
? '123'
: ''
}
primary
style={
description
? textFieldWithLabelRightButtonMargins
: textFieldRightButtonMargins
}
onClick={this._openExpressionPopover}
/>
)}
renderButton={style => (
<React.Fragment>
{!this.props.isInline &&
this.props.renderExtraButton &&
this.props.renderExtraButton({
style,
})}
{!this.props.isInline && (
<RaisedButton
icon={<Functions />}
label={
expressionType === 'string'
? '"ABC"'
: expressionType === 'number'
? '123'
: ''
}
primary
style={style}
onClick={this._openExpressionPopover}
/>
)}
</React.Fragment>
)}
/>
{this.state.parametersDialogOpen && this.state.selectedExpressionInfo && (
<ExpressionParametersEditorDialog
open={true}
@@ -380,7 +380,7 @@ export default class ExpressionField extends React.Component<Props, State> {
parameterRenderingService={parameterRenderingService}
/>
)}
</div>
</React.Fragment>
);
}
}

View File

@@ -7,17 +7,7 @@ import { type ParameterFieldProps } from './ParameterFieldCommons';
import classNames from 'classnames';
import { icon } from '../EventsTree/ClassNames';
import SemiControlledAutoComplete from '../../UI/SemiControlledAutoComplete';
import {
textFieldRightButtonMargins,
textFieldWithLabelRightButtonMargins,
} from './GenericExpressionField';
const styles = {
container: {
display: 'flex',
alignItems: 'flex-start',
},
};
import { TextFieldWithButtonLayout } from '../../UI/Layout';
type Props = {
...ParameterFieldProps,
@@ -47,35 +37,35 @@ export default class VariableField extends Component<Props, {||}> {
: undefined;
return (
<div style={styles.container}>
<SemiControlledAutoComplete
floatingLabelText={description}
fullWidth
value={value}
onChange={onChange}
dataSource={enumerateVariables(variablesContainer).map(
variableName => ({
text: variableName,
value: variableName,
})
)}
openOnFocus={!isInline}
ref={field => (this._field = field)}
/>
{onOpenDialog && !isInline && (
<RaisedButton
icon={<OpenInNew />}
disabled={!this.props.variablesContainer}
primary
style={
description
? textFieldWithLabelRightButtonMargins
: textFieldRightButtonMargins
}
onClick={onOpenDialog}
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledAutoComplete
floatingLabelText={description}
fullWidth
value={value}
onChange={onChange}
dataSource={enumerateVariables(variablesContainer).map(
variableName => ({
text: variableName,
value: variableName,
})
)}
openOnFocus={!isInline}
ref={field => (this._field = field)}
/>
)}
</div>
renderButton={style =>
onOpenDialog && !isInline ? (
<RaisedButton
icon={<OpenInNew />}
disabled={!this.props.variablesContainer}
primary
style={style}
onClick={onOpenDialog}
/>
) : null
}
/>
);
}
}

View File

@@ -6,7 +6,6 @@ import React, { PureComponent } from 'react';
import Background from '../UI/Background';
import TextField from '../UI/TextField';
import { Line, Column, Spacer } from '../UI/Grid';
import FlatButton from '../UI/FlatButton';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import IconButton from '../UI/IconButton';
@@ -16,6 +15,7 @@ import {
type SearchInEventsInputs,
type ReplaceInEventsInputs,
} from './EventsSearcher';
import RaisedButton from '../UI/RaisedButton';
type Props = {|
onSearchInEvents: SearchInEventsInputs => void,
@@ -104,15 +104,17 @@ export default class SearchPanel extends PureComponent<Props, State> {
<Column>
<Line alignItems="baseline">
<TextField
margin="none"
ref={_searchTextField =>
(this.searchTextField = _searchTextField)
}
hintText={t`Text to search`}
hintText={t`Text to search in parameters`}
onChange={(e, searchText) => this.setState({ searchText })}
value={searchText}
fullWidth
/>
<FlatButton
<Spacer />
<RaisedButton
disabled={!searchText}
primary
label={<Trans>Search</Trans>}
@@ -121,12 +123,14 @@ export default class SearchPanel extends PureComponent<Props, State> {
</Line>
<Line alignItems="baseline">
<TextField
hintText={t`Text to replace`}
margin="none"
hintText={t`Text to replace in parameters`}
onChange={(e, replaceText) => this.setState({ replaceText })}
value={replaceText}
fullWidth
/>
<FlatButton
<Spacer />
<RaisedButton
disabled={
!replaceText ||
!searchText ||

View File

@@ -90,6 +90,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
style={styles.checkbox}
/>
<ResourceSelector
margin="none"
project={project}
resourceSources={resourceSources}
onChooseResource={onChooseResource}
@@ -110,6 +111,8 @@ export default class TextEditor extends React.Component<EditorProps, void> {
<Column expand>
<Line>
<SemiControlledTextField
floatingLabelText={<Trans>Initial text to display</Trans>}
floatingLabelFixed
commitOnBlur
hintText={t`Enter the text to be displayed by the object`}
fullWidth

View File

@@ -13,6 +13,7 @@ import { withSerializableObject } from '../Utils/SerializableObjectEditorContain
import { Column, Line } from '../UI/Grid';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import Text from '../UI/Text';
import MiniToolbar, { MiniToolbarText } from '../UI/MiniToolbar';
type StateType = {|
currentTab: string,
@@ -83,25 +84,25 @@ export class ObjectEditorDialog extends Component<*, StateType> {
</div>
}
>
<Line alignItems="baseline">
<Column>
<Text noShrink>Object Name:</Text>
</Column>
<Column expand>
<SemiControlledTextField
fullWidth
commitOnBlur
margin="none"
value={this.state.newObjectName}
hintText={t`Object Name`}
onChange={text => {
if (this.props.canRenameObject(text)) {
this.setState({ newObjectName: text });
}
}}
/>
</Column>
</Line>
<MiniToolbar alignItems="baseline">
<MiniToolbarText>
<Trans>Object Name:</Trans>
</MiniToolbarText>
<SemiControlledTextField
fullWidth
commitOnBlur
margin="none"
value={this.state.newObjectName}
hintText={t`Object Name`}
onChange={text => {
if (text === this.state.newObjectName) return;
if (this.props.canRenameObject(text)) {
this.setState({ newObjectName: text });
}
}}
/>
</MiniToolbar>
{currentTab === 'properties' && EditorComponent && (
<EditorComponent
object={this.props.object}

View File

@@ -241,6 +241,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
return (
<Dialog
title={<Trans>Project icons</Trans>}
actions={actions}
open={this.props.open}
onRequestClose={this.props.onClose}

View File

@@ -164,6 +164,7 @@ class ProjectPropertiesDialog extends React.Component<Props, State> {
key="help"
/>,
]}
title={<Trans>Project properties</Trans>}
open={this.props.open}
onRequestClose={this.props.onClose}
>

View File

@@ -1,4 +1,5 @@
// @flow
import { Trans } from '@lingui/macro';
import * as React from 'react';
import {
ResponsiveWindowMeasurer,
@@ -13,13 +14,14 @@ import FlatButton from '../UI/FlatButton';
import SelectField from '../UI/SelectField';
import SelectOption from '../UI/SelectOption';
import Edit from '@material-ui/icons/Edit';
import IconButton from '../UI/IconButton';
import {
type ResourceKind,
type ResourceSource,
type ChooseResourceFunction,
} from '../ResourcesList/ResourceSource.flow';
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
import { TextFieldWithButtonLayout } from '../UI/Layout';
import RaisedButton from '../UI/RaisedButton';
// An "instance" here is the objects for which properties are shown
export type Instance = Object; // This could be improved using generics.
@@ -216,32 +218,39 @@ export default class PropertiesEditor extends React.Component<Props, {||}> {
} else {
const { onEditButtonClick, setValue } = field;
return (
<div style={styles.fieldContainer} key={field.name}>
<SemiControlledTextField
value={getFieldValue(
this.props.instances,
field,
'(Multiple values)'
)}
id={field.name}
floatingLabelText={getFieldLabel(this.props.instances, field)}
floatingLabelFixed
onChange={newValue => {
this.props.instances.forEach(i => setValue(i, newValue || ''));
this._onInstancesModified(this.props.instances);
}}
style={styles.field}
disabled={field.disabled}
/>
{onEditButtonClick && (
<IconButton
disabled={this.props.instances.length !== 1}
onClick={() => onEditButtonClick(this.props.instances[0])}
>
<Edit />
</IconButton>
<TextFieldWithButtonLayout
key={field.name}
renderTextField={() => (
<SemiControlledTextField
value={getFieldValue(
this.props.instances,
field,
'(Multiple values)'
)}
id={field.name}
floatingLabelText={getFieldLabel(this.props.instances, field)}
floatingLabelFixed
onChange={newValue => {
this.props.instances.forEach(i => setValue(i, newValue || ''));
this._onInstancesModified(this.props.instances);
}}
style={styles.field}
disabled={field.disabled}
/>
)}
</div>
renderButton={style =>
onEditButtonClick ? (
<RaisedButton
style={style}
primary
disabled={this.props.instances.length !== 1}
icon={<Edit />}
label={<Trans>Edit</Trans>}
onClick={() => onEditButtonClick(this.props.instances[0])}
/>
) : null
}
/>
);
}
};

View File

@@ -1,12 +1,10 @@
// @flow
import { Trans } from '@lingui/macro';
import * as React from 'react';
import IconButton from '../UI/IconButton';
import SemiControlledAutoComplete, {
type DataSource,
} from '../UI/SemiControlledAutoComplete';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import { Line } from '../UI/Grid';
import BackspaceIcon from '@material-ui/icons/Backspace';
import Add from '@material-ui/icons/Add';
import Brush from '@material-ui/icons/Brush';
@@ -19,6 +17,9 @@ import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEd
import ResourcesLoader from '../ResourcesLoader';
import { applyResourceDefaults } from './ResourceUtils';
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
import RaisedButtonWithMenu from '../UI/RaisedButtonWithMenu';
import { TextFieldWithButtonLayout } from '../UI/Layout';
import IconButton from '../UI/IconButton';
type Props = {|
project: gdProject,
@@ -33,7 +34,7 @@ type Props = {|
onChange: string => void,
floatingLabelText?: React.Node,
hintText?: MessageDescriptor,
margin?: 'none' | 'normal',
margin?: 'none' | 'dense',
|};
type State = {|
@@ -41,10 +42,6 @@ type State = {|
resourceName: string,
|};
const styles = {
container: { display: 'flex', flex: 1, alignItems: 'flex-end' },
};
export default class ResourceSelector extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
@@ -259,8 +256,10 @@ export default class ResourceSelector extends React.Component<Props, State> {
externalEditor => externalEditor.kind === this.props.resourceKind
);
return (
<div style={styles.container}>
<Line nomargin expand>
<TextFieldWithButtonLayout
noFloatingLabelText={!this.props.floatingLabelText}
margin={this.props.margin}
renderTextField={() => (
<SemiControlledAutoComplete
floatingLabelText={this.props.floatingLabelText}
hintText={this.props.hintText}
@@ -273,32 +272,42 @@ export default class ResourceSelector extends React.Component<Props, State> {
margin={this.props.margin}
ref={autoComplete => (this._autoComplete = autoComplete)}
/>
{this.props.canBeReset && (
<IconButton
onClick={() => {
this._onResetResourceName();
}}
>
<BackspaceIcon />
</IconButton>
)}
{!!externalEditors.length && (
<ElementWithMenu
element={
<IconButton>
<Brush />
</IconButton>
}
buildMenuTemplate={() =>
externalEditors.map(externalEditor => ({
label: externalEditor.displayName,
click: () => this._editWith(externalEditor),
}))
}
/>
)}
</Line>
</div>
)}
renderButton={style => (
<React.Fragment>
{this.props.canBeReset && (
<IconButton
size="small"
onClick={() => {
this._onResetResourceName();
}}
>
<BackspaceIcon />
</IconButton>
)}
{!!externalEditors.length ? (
<RaisedButtonWithMenu
style={style}
icon={<Brush />}
label={
this.state.resourceName ? (
<Trans>Edit</Trans>
) : (
<Trans>Create</Trans>
)
}
primary
buildMenuTemplate={() =>
externalEditors.map(externalEditor => ({
label: externalEditor.displayName,
click: () => this._editWith(externalEditor),
}))
}
/>
) : null}
</React.Fragment>
)}
/>
);
}
}

View File

@@ -26,7 +26,7 @@ type Props = {|
const styles = {
container: { flex: 1, display: 'flex', alignItems: 'flex-end' },
selectorContainer: { flex: 1 },
resourceThumbnail: { marginLeft: 10 },
resourceThumbnail: { marginLeft: 10, marginBottom: 4 },
};
const ResourceSelectorWithThumbnail = ({

View File

@@ -1,6 +1,11 @@
import React from 'react';
const marginsSize = 4;
/**
* A Line in the standard GDevelop grid to position components.
* Check `Layout` first to see if there is already a layout made
* specifically for your components (like TextFieldWithButton).
*/
export const Line = props => (
<div
style={{
@@ -16,6 +21,11 @@ export const Line = props => (
</div>
);
/**
* A Column in the standard GDevelop grid to position components.
* Check `Layout` first to see if there is already a layout made
* specifically for your components (like TextFieldWithButton).
*/
export const Column = props => (
<div
style={{
@@ -32,6 +42,11 @@ export const Column = props => (
</div>
);
/**
* A Spacer in the standard GDevelop grid to position components.
* Check `Layout` first to see if there is already a layout made
* specifically for your components (like TextFieldWithButton).
*/
export const Spacer = props => (
<span
style={{

View File

@@ -29,7 +29,11 @@ type Props = {|
transform?: string,
transition?: string,
opacity?: number,
margin?: number,
marginRight?: number,
marginLeft?: number,
marginTop?: number,
marginBottom?: number,
|},
size?: 'small',

View File

@@ -0,0 +1,59 @@
// @flow
import * as React from 'react';
type TextFieldWithButtonLayoutProps = {|
renderTextField: () => React.Node,
renderButton: (style: {|
marginTop?: number,
marginBottom?: number,
marginLeft?: number,
marginRight?: number,
margin?: number,
|}) => React.Node,
margin?: 'none' | 'dense',
noFloatingLabelText?: boolean,
|};
const textFieldWithButtonLayoutStyles = {
container: {
flex: 1,
display: 'flex',
alignItems: 'flex-start', // Align from the top to stay at the same position when error/multiline
},
filledTextFieldRightButtonMargins: {
marginTop: 24, // Properly align with the text field (only dense "filled" text fields supported)
marginLeft: 10,
},
standardTextFieldWithLabelRightButtonMargins: {
marginTop: 17, // Properly align with the text field (only dense "standard" text fields supported)
marginLeft: 10,
},
standardTextFieldWithoutLabelRightButtonMargins: {
marginTop: 0, // Properly align with the text field (only dense "standard" text fields supported)
marginLeft: 10,
},
};
/**
* Position a button on the right of a TextField.
* Only compatible with TextField with a label.
*/
export const TextFieldWithButtonLayout = ({
margin,
noFloatingLabelText,
renderTextField,
renderButton,
}: TextFieldWithButtonLayoutProps) => {
return (
<div style={textFieldWithButtonLayoutStyles.container}>
{renderTextField()}
{renderButton(
margin === 'none'
? noFloatingLabelText
? textFieldWithButtonLayoutStyles.standardTextFieldWithoutLabelRightButtonMargins
: textFieldWithButtonLayoutStyles.standardTextFieldWithLabelRightButtonMargins
: textFieldWithButtonLayoutStyles.filledTextFieldRightButtonMargins
)}
</div>
);
};

View File

@@ -4,8 +4,8 @@ import { t } from '@lingui/macro';
import React, { PureComponent } from 'react';
import TextField from '../TextField';
import FlatButton from '../FlatButton';
import optionalRequire from '../../Utils/OptionalRequire.js';
import RaisedButton from '../RaisedButton';
const electron = optionalRequire('electron');
const dialog = electron ? electron.remote.dialog : null;
@@ -30,7 +30,6 @@ type Props = {|
message: string,
defaultPath?: string,
fullWidth?: boolean,
floatingLabelText?: string,
filters: Array<{
name: string,
extensions: Array<string>,
@@ -65,15 +64,14 @@ export default class LocalFilePicker extends PureComponent<Props, *> {
}}
>
<TextField
margin="none"
style={styles.textField}
floatingLabelText={this.props.floatingLabelText}
floatingLabelFixed
type="text"
hintText={t`Click to choose`}
hintText={t`Choose a file`}
value={this.props.value}
onChange={(event, value) => this.props.onChange(value)}
/>
<FlatButton
<RaisedButton
label={<Trans>Choose</Trans>}
style={styles.button}
onClick={this.onChooseFolder}

View File

@@ -6,7 +6,7 @@ import { type I18n as I18nType } from '@lingui/core';
import React, { PureComponent } from 'react';
import TextField from '../TextField';
import FlatButton from '../FlatButton';
import RaisedButton from '../RaisedButton';
import optionalRequire from '../../Utils/OptionalRequire.js';
const electron = optionalRequire('electron');
const dialog = electron ? electron.remote.dialog : null;
@@ -31,7 +31,6 @@ type Props = {|
onChange: string => void,
defaultPath?: string,
fullWidth?: boolean,
floatingLabelText?: string,
|};
type TitleAndMessage = {|
@@ -83,31 +82,32 @@ export default class LocalFolderPicker extends PureComponent<Props, {||}> {
render() {
return (
<I18n>
{({ i18n }) => (
<div
style={{
...styles.container,
width: this.props.fullWidth ? '100%' : undefined,
}}
>
<TextField
style={styles.textField}
floatingLabelText={this.props.floatingLabelText}
floatingLabelFixed
type="text"
hintText={t`Click to choose`}
value={this.props.value}
onChange={(event, value) => this.props.onChange(value)}
/>
<FlatButton
label={<Trans>Choose folder</Trans>}
style={styles.button}
onClick={() =>
this._onChooseFolder(this._getTitleAndMessage(i18n))
}
/>
</div>
)}
{({ i18n }) => {
const titleAndMessage = this._getTitleAndMessage(i18n);
return (
<div
style={{
...styles.container,
width: this.props.fullWidth ? '100%' : undefined,
}}
>
<TextField
margin="none"
style={styles.textField}
type="text"
hintText={titleAndMessage.title}
value={this.props.value}
onChange={(event, value) => this.props.onChange(value)}
/>
<RaisedButton
label={<Trans>Choose folder</Trans>}
primary={false}
style={styles.button}
onClick={() => this._onChooseFolder(titleAndMessage)}
/>
</div>
);
}}
</I18n>
);
}

View File

@@ -37,7 +37,9 @@ const toolbarTextStyle = {
};
export const MiniToolbarText = ({ children }) => (
<Text style={toolbarTextStyle}>{children}</Text>
<Text noShrink style={toolbarTextStyle}>
{children}
</Text>
);
export default MiniToolbar;

View File

@@ -5,9 +5,8 @@ import { Spacer } from './Grid';
// We support a subset of the props supported by Material-UI v0.x RaisedButton
// They should be self descriptive - refer to Material UI docs otherwise.
type Props = {|
export type RaisedButtonPropsWithoutOnClick = {|
label?: React.Node,
onClick: ?() => void,
primary?: boolean,
disabled?: boolean,
fullWidth?: boolean,
@@ -22,6 +21,11 @@ type Props = {|
labelPosition?: 'before',
|};
type Props = {
...RaisedButtonPropsWithoutOnClick,
onClick: ?() => void,
};
/**
* A raised button based on Material-UI button.
*/

View File

@@ -0,0 +1,33 @@
// @flow
import * as React from 'react';
import RaisedButton, {
type RaisedButtonPropsWithoutOnClick,
} from './RaisedButton';
import ElementWithMenu from './Menu/ElementWithMenu';
// We support a subset of the props supported by Material-UI v0.x RaisedButton
// They should be self descriptive - refer to Material UI docs otherwise.
type Props = {|
...RaisedButtonPropsWithoutOnClick,
buildMenuTemplate: () => Array<any>,
|};
const shouldNeverBeCalled = () => {
throw new Error('This RaisedButtonWithMenu onClick should never be called');
};
/**
* A raised button based on Material-UI button, that has a menu displayed when clicked.
*/
const RaisedButtonWithMenu = (props: Props) => {
const { buildMenuTemplate, ...otherProps } = props;
return (
<ElementWithMenu
element={<RaisedButton {...otherProps} onClick={shouldNeverBeCalled} />}
buildMenuTemplate={buildMenuTemplate}
/>
);
};
export default RaisedButtonWithMenu;

View File

@@ -76,7 +76,8 @@ export default class SelectField extends React.Component<Props, {||}> {
{({ i18n }) => (
<TextField
select
margin={props.margin || 'normal'}
variant={props.margin === 'none' ? 'standard' : 'filled'}
margin={props.margin || 'dense'}
disabled={props.disabled}
fullWidth={props.fullWidth}
label={props.floatingLabelText}

View File

@@ -27,7 +27,6 @@ export type DataSource = Array<
const styles = {
container: {
flexGrow: 1,
position: 'relative',
},
inputRoot: {
@@ -126,7 +125,7 @@ type Props = {|
floatingLabelText?: React.Node,
hintText?: MessageDescriptor | string,
fullWidth?: boolean,
margin?: 'none' | 'normal',
margin?: 'none' | 'dense',
textFieldStyle?: Object,
openOnFocus?: boolean,
|};
@@ -271,7 +270,12 @@ export default class SemiControlledAutoComplete extends React.Component<
};
return (
<div style={styles.container}>
<div
style={{
...styles.container,
flexGrow: props.fullWidth ? 1 : undefined,
}}
>
{renderTextField({
disabled: props.disabled,
label: props.floatingLabelText,
@@ -297,7 +301,8 @@ export default class SemiControlledAutoComplete extends React.Component<
// Style:
style: props.textFieldStyle,
fullWidth: props.fullWidth,
margin: props.margin || 'normal',
variant: props.margin === 'none' ? 'standard' : 'filled',
margin: props.margin || 'dense',
inputRef: this._input,
})}

View File

@@ -25,7 +25,7 @@ type Props = {|
type?: 'text' | 'number',
// Some TextField props that can be reused:
margin?: 'none' | 'normal',
margin?: 'none' | 'dense',
disabled?: boolean,
errorText?: React.Node,
floatingLabelFixed?: boolean,

View File

@@ -72,7 +72,7 @@ type Props = {|
rowsMax?: number,
// Styling:
margin?: 'none' | 'normal',
margin?: 'none' | 'dense',
fullWidth?: boolean,
style?: {|
fontSize?: 18,
@@ -128,6 +128,7 @@ export default class TextField extends React.Component<Props, {||}> {
<I18n>
{({ i18n }) => (
<MUITextField
variant={props.margin === 'none' ? 'standard' : 'filled'}
// Value and change handling:
type={props.type !== undefined ? props.type : undefined}
value={props.value !== undefined ? props.value : undefined}
@@ -157,7 +158,7 @@ export default class TextField extends React.Component<Props, {||}> {
rows={props.rows}
rowsMax={props.rowsMax}
// Styling:
margin={props.margin || 'normal'}
margin={props.margin || 'dense'}
fullWidth={props.fullWidth}
InputProps={{
disableUnderline:

View File

@@ -0,0 +1,34 @@
// @flow
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
/**
* Fake "external editors" to be used in Storybook.
*/
const fakeResourceExternalEditors: Array<ResourceExternalEditor> = [
{
name: 'fake-image-editor',
displayName: 'Edit with Super Image Editor',
kind: 'image',
edit: options => {
console.log('Open the image editor with these options:', options);
},
},
{
name: 'fake-audio-editor',
displayName: 'Create/Edit a Sound effect with Super Audio Editor',
kind: 'audio',
edit: options => {
console.log('Open the audio editor with these options:', options);
},
},
{
name: 'fake-json-editor',
displayName: 'Create/Edit a Dialogue Tree with Super JSON Dialogue Editor',
kind: 'json',
edit: options => {
console.log('Open the json editor with these options:', options);
},
},
];
export default fakeResourceExternalEditors;

View File

@@ -147,7 +147,7 @@ import SubscriptionPendingDialog from '../Profile/SubscriptionPendingDialog';
import Dialog from '../UI/Dialog';
import MiniToolbar, { MiniToolbarText } from '../UI/MiniToolbar';
import NewObjectDialog from '../ObjectsList/NewObjectDialog';
import { Column } from '../UI/Grid';
import { Column, Line } from '../UI/Grid';
import DragAndDropTestBed from './DragAndDropTestBed';
import EditorMosaic from '../UI/EditorMosaic';
import FlatButton from '../UI/FlatButton';
@@ -162,6 +162,16 @@ import GoogleDriveSaveAsDialog from '../ProjectsStorage/GoogleDriveStorageProvid
import OpenConfirmDialog from '../ProjectsStorage/OpenConfirmDialog';
import CreateAccountDialog from '../Profile/CreateAccountDialog';
import BrowserPreviewErrorDialog from '../Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewErrorDialog';
import RaisedButton from '../UI/RaisedButton';
import Text from '../UI/Text';
import ToolbarIcon from '../UI/ToolbarIcon';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import IconButton from '../UI/IconButton';
import FilterList from '@material-ui/icons/FilterList';
import Brush from '@material-ui/icons/Brush';
import RaisedButtonWithMenu from '../UI/RaisedButtonWithMenu';
import fakeResourceExternalEditors from './FakeResourceExternalEditors';
import { TextFieldWithButtonLayout } from '../UI/Layout';
// No i18n in this file
@@ -194,10 +204,117 @@ const {
const Placeholder = () => <div>Placeholder component</div>;
const buildFakeMenuTemplate = () => [
{
label: 'Option 1',
click: action('click option 1'),
},
{ type: 'separator' },
{
label: 'Option 2',
click: action('click option 2'),
},
];
storiesOf('Welcome', module).add('to Storybook', () => (
<Welcome showApp={linkTo('Button')} />
));
storiesOf('UI Building Blocks/Buttons', module)
.addDecorator(muiDecorator)
.add('default', () => (
<Column>
<Line>
<Text>Buttons:</Text>
</Line>
<Line>
<RaisedButton label="Raised button" onClick={action('onClick')} />
<RaisedButton
label="Primary Raised button"
primary
onClick={action('onClick')}
/>
</Line>
<Line>
<FlatButton label="Flat button" onClick={action('onClick')} />
<FlatButton
label="Primary Flat button"
primary
onClick={action('onClick')}
/>
</Line>
<Line>
<Text>Buttons with menus:</Text>
</Line>
<Line>
<RaisedButton
label="Traditional Raised button"
onClick={action('onClick')}
/>
<RaisedButtonWithMenu
label="Button with menu"
buildMenuTemplate={buildFakeMenuTemplate}
/>
<RaisedButtonWithMenu
label="... and with icon"
icon={<Brush />}
buildMenuTemplate={buildFakeMenuTemplate}
/>
</Line>
<Line>
<Text>Icons with menu:</Text>
</Line>
<Line>
<ElementWithMenu
element={
<ToolbarIcon
src="res/ribbon_default/bug32.png"
tooltip={'ToolbarIcon with menu'}
/>
}
buildMenuTemplate={buildFakeMenuTemplate}
/>
<ElementWithMenu
element={
<IconButton>
<FilterList />
</IconButton>
}
buildMenuTemplate={buildFakeMenuTemplate}
/>
</Line>
<Line>
<Text>In a mini toolbar:</Text>
</Line>
<Line>
<MiniToolbar>
<MiniToolbarText>Some text:</MiniToolbarText>
<IconButton>
<Brush />
</IconButton>
<ElementWithMenu
element={
<IconButton>
<FilterList />
</IconButton>
}
buildMenuTemplate={() => [
{
label: 'Option 1',
click: action('click option 1'),
},
{ type: 'separator' },
{
label: 'Option 2',
click: action('click option 2'),
},
]}
/>
</MiniToolbar>
</Line>
</Column>
));
storiesOf('UI Building Blocks/SemiControlledTextField', module)
.addDecorator(muiDecorator)
.add('default', () => (
@@ -258,6 +375,25 @@ storiesOf('UI Building Blocks/SemiControlledTextField', module)
</React.Fragment>
)}
/>
))
.add('reduced margin, in a MiniToolbar', () => (
<ValueStateHolder
initialValue={'Choice 6'}
render={(value, onChange) => (
<React.Fragment>
<MiniToolbar>
<MiniToolbarText>Please enter something:</MiniToolbarText>
<SemiControlledTextField
margin="none"
value={value}
onChange={onChange}
commitOnBlur
/>
</MiniToolbar>
<p>State value is {value}</p>
</React.Fragment>
)}
/>
));
storiesOf('UI Building Blocks/DragAndDrop', module).add('test bed', () => (
@@ -448,6 +584,218 @@ storiesOf('UI Building Blocks/SearchBar', module)
/>
));
storiesOf('UI Building Blocks/Layout', module)
.addDecorator(muiDecorator)
.add('Empty text field', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledTextField
floatingLabelText="Hello world"
value=""
onChange={() => {}}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty text field, margin=none', () => (
<TextFieldWithButtonLayout
margin="none"
renderTextField={() => (
<SemiControlledTextField
margin="none"
floatingLabelText="Hello world"
value=""
onChange={() => {}}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty auto complete field', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledAutoComplete
floatingLabelText="Hello world"
value={''}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty auto complete field, noFloatingLabelText', () => (
<TextFieldWithButtonLayout
noFloatingLabelText
renderTextField={() => (
<SemiControlledAutoComplete
value={''}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty auto complete field, margin=none', () => (
<TextFieldWithButtonLayout
margin="none"
renderTextField={() => (
<SemiControlledAutoComplete
margin="none"
floatingLabelText="Hello world"
value={''}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty auto complete field, margin=none, noFloatingLabelText', () => (
<TextFieldWithButtonLayout
margin="none"
noFloatingLabelText
renderTextField={() => (
<SemiControlledAutoComplete
margin="none"
value={''}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Empty auto complete field, margin=none, noFloatingLabelText, with a small IconButton', () => (
<TextFieldWithButtonLayout
margin="none"
noFloatingLabelText
renderTextField={() => (
<SemiControlledAutoComplete
margin="none"
value={''}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<IconButton size="small">
<Brush />
</IconButton>
)}
/>
))
.add('Filled text field', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledTextField
floatingLabelText="Hello"
value="123"
onChange={() => {}}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Filled text field, full width', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledTextField
floatingLabelText="Hello"
value="123"
onChange={() => {}}
fullWidth
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Filled multiline text field', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledTextField
floatingLabelText="Hello"
multiLine
value={'123\n456\n789\nblablabla bla bla'}
onChange={() => {}}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Filled auto complete field', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledAutoComplete
floatingLabelText="Hello world"
value={'Choice 5'}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
))
.add('Filled auto complete field, full width', () => (
<TextFieldWithButtonLayout
renderTextField={() => (
<SemiControlledAutoComplete
floatingLabelText="Hello world"
value={'Choice 5'}
onChange={() => {}}
dataSource={[1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => ({
text: `Choice ${i}`,
value: `Choice ${i}`,
}))}
fullWidth
/>
)}
renderButton={style => (
<RaisedButton style={style} label="Button" onClick={() => {}} />
)}
/>
));
storiesOf('UI Building Blocks/Background', module)
.addDecorator(muiDecorator)
.add('default', () => <Background>Hello world</Background>);
@@ -1143,7 +1491,7 @@ storiesOf('ParameterFields', module)
parameterRenderingService={ParameterRenderingService}
resourceSources={[]}
onChooseResource={() => Promise.reject('unimplemented')}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
/>
)}
/>
@@ -2028,7 +2376,7 @@ storiesOf('EventsSheet', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onOpenDebugger={action('open debugger')}
onOpenLayout={action('open layout')}
onOpenSettings={action('open settings')}
@@ -2056,7 +2404,7 @@ storiesOf('EventsSheet', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onOpenDebugger={action('open debugger')}
onOpenLayout={action('open layout')}
onOpenSettings={action('open settings')}
@@ -2239,7 +2587,7 @@ storiesOf('InstructionEditor', module)
objectsContainer={testLayout}
isCondition
instruction={testInstruction}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onChooseResource={() => {
action('onChooseResource');
return Promise.reject();
@@ -2258,7 +2606,7 @@ storiesOf('InstructionEditor', module)
objectsContainer={testLayout}
isCondition
instruction={testInstruction}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onChooseResource={() => {
action('onChooseResource');
return Promise.reject();
@@ -2282,7 +2630,7 @@ storiesOf('NewInstructionEditorDialog', module)
isCondition
isNewInstruction={false}
instruction={testInstruction}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onChooseResource={() => {
action('onChooseResource');
return Promise.reject();
@@ -2303,7 +2651,7 @@ storiesOf('NewInstructionEditorDialog', module)
isCondition
isNewInstruction={false}
instruction={testInstruction}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onChooseResource={() => {
action('onChooseResource');
return Promise.reject();
@@ -2324,7 +2672,7 @@ storiesOf('NewInstructionEditorDialog', module)
isCondition
isNewInstruction={true}
instruction={testInstruction}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
onChooseResource={() => {
action('onChooseResource');
return Promise.reject();
@@ -2348,7 +2696,7 @@ storiesOf('TextEditor', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
/>
</SerializedObjectDisplay>
));
@@ -2365,7 +2713,7 @@ storiesOf('TiledSpriteEditor', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
/>
</SerializedObjectDisplay>
));
@@ -2382,7 +2730,7 @@ storiesOf('PanelSpriteEditor', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
/>
</SerializedObjectDisplay>
));
@@ -2399,7 +2747,7 @@ storiesOf('SpriteEditor and related editors', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
/>
</SerializedObjectDisplay>
))
@@ -2996,7 +3344,7 @@ storiesOf('ResourceSelector (and ResourceSelectorWithThumbnail)', module)
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="resource-that-does-not-exists-in-the-project"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
@@ -3008,7 +3356,20 @@ storiesOf('ResourceSelector (and ResourceSelectorWithThumbnail)', module)
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="icon128.png"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
/>
))
.add('image resource, no margin', () => (
<ResourceSelector
margin="none"
resourceKind="image"
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="icon128.png"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
@@ -3020,7 +3381,7 @@ storiesOf('ResourceSelector (and ResourceSelectorWithThumbnail)', module)
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
resourceName="icon128.png"
onChange={action('on change')}
/>
@@ -3031,11 +3392,38 @@ storiesOf('ResourceSelector (and ResourceSelectorWithThumbnail)', module)
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="fake-audio1.mp3"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
/>
))
.add('font resource, with reset button', () => (
<ResourceSelector
canBeReset
resourceKind="font"
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="font.otf"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
/>
))
.add('font resource, no margin, with reset button', () => (
<ResourceSelector
canBeReset
margin="none"
resourceKind="font"
project={project}
resourceSources={[]}
onChooseResource={() => Promise.reject('Unimplemented')}
resourceExternalEditors={fakeResourceExternalEditors}
initialResourceName="font.otf"
onChange={action('on change')}
resourcesLoader={ResourcesLoader}
/>
));
storiesOf('ResourcesList', module)
@@ -3139,7 +3527,7 @@ storiesOf('EventsFunctionsExtensionEditor/index', module)
onChooseResource={source =>
action('Choose resource from source', source)
}
resourceExternalEditors={[]}
resourceExternalEditors={fakeResourceExternalEditors}
openInstructionOrExpression={action('open instruction or expression')}
initiallyFocusedFunctionName={null}
initiallyFocusedBehaviorName={null}