Compare commits

..

2 Commits

Author SHA1 Message Date
AlexandreSi
9995c5ee82 Add explanation of date accessors in long description 2022-12-05 10:10:26 +01:00
AlexandreSi
3bebc57c88 Add option to get full year 2022-12-05 09:58:44 +01:00
161 changed files with 2184 additions and 4552 deletions

View File

@@ -227,12 +227,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
.AddCodeOnlyParameter("currentScene", "")
.AddParameter(
"stringWithSelector",
_("Date part"),
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"fullyear\", "
"\"wday\", \"mday\", \"yday\", \"timestamp\"]")
.SetParameterLongDescription(
_("Hour: hour - Minutes: min - Seconds: sec - Day of month: "
"mday - Months since January: mon - Year since 1900: year - Days "
"since Sunday: wday - Days since Jan 1st: yday - Timestamp (ms): "
"timestamp\""),
"[\"hour\", \"min\", \"sec\", \"mon\", \"year\", \"wday\", \"mday\", "
"\"yday\", \"timestamp\"]");
"mday - Months since January (January being 0): mon - Year: "
"fullyear - Year since 1900: year - Days since Sunday: wday - Days "
"since Jan 1st: yday - Timestamp (ms): timestamp\""));
}
} // namespace gd

View File

@@ -773,18 +773,15 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
const gd::String& ignored_characters =
EventsRefactorer::searchIgnoredCharacters;
if (inEventSentences) {
// Remove ignored characters only when searching in event sentences.
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
}
search.replace_if(
search.begin(),
search.end(),
[ignored_characters](const char& c) {
return ignored_characters.find(c) != gd::String::npos;
},
"");
search = search.LeftTrim().RightTrim();
search.RemoveConsecutiveOccurrences(search.begin(), search.end(), ' ');
for (std::size_t i = 0; i < events.size(); ++i) {
bool eventAddedInResults = false;

View File

@@ -408,30 +408,6 @@ namespace gdjs {
);
}
/**
* Callback called when the game is paused.
*/
sendGamePaused(): void {
this._sendMessage(
circularSafeStringify({
command: 'game.paused',
payload: null,
})
);
}
/**
* Callback called when the game is resumed.
*/
sendGameResumed(): void {
this._sendMessage(
circularSafeStringify({
command: 'game.resumed',
payload: null,
})
);
}
/**
* Send profiling results.
* @param framesAverageMeasures The measures made for each frames.

View File

@@ -221,6 +221,8 @@ namespace gdjs {
return now.getDate();
} else if (what === 'mon') {
return now.getMonth();
} else if (what === 'fullyear') {
return now.getFullYear();
} else if (what === 'year') {
//Conform to the C way of returning years.
return now.getFullYear() - 1900;

View File

@@ -493,13 +493,7 @@ namespace gdjs {
* @param enable true to pause the game, false to unpause
*/
pause(enable: boolean) {
if (this._paused === enable) return;
this._paused = enable;
if (this._debuggerClient) {
if (this._paused) this._debuggerClient.sendGamePaused();
else this._debuggerClient.sendGameResumed();
}
}
/**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -25,7 +25,6 @@
],
"start_url": "./index.html",
"display": "standalone",
"display_override": ["window-controls-overlay"],
"theme_color": "#252525",
"background_color": "#f0f0f0"
}

View File

@@ -225,6 +225,11 @@
}
},
"home": {
"header": {
"background-color": {
"value": "{theme.surface.canvas.background-color.value}"
}
},
"separator": {
"color": {
"value": "#32323B"

View File

@@ -11,7 +11,7 @@ import ButtonBase from '@material-ui/core/ButtonBase';
import Text from '../../UI/Text';
import { Trans } from '@lingui/macro';
import { Column, Line } from '../../UI/Grid';
import FlatButtonWithSplitMenu from '../../UI/FlatButtonWithSplitMenu';
import RaisedButtonWithSplitMenu from '../../UI/RaisedButtonWithSplitMenu';
import { getIDEVersion } from '../../Version';
import { ExampleThumbnailOrIcon } from './ExampleThumbnailOrIcon';
import optionalRequire from '../../Utils/OptionalRequire';
@@ -125,7 +125,7 @@ export const ExampleListItem = ({
</ButtonBase>
<Column noMargin justifyContent="flex-end">
<Line noMargin justifyContent="flex-end">
<FlatButtonWithSplitMenu
<RaisedButtonWithSplitMenu
label={<Trans>Open</Trans>}
disabled={isOpening || !isCompatible}
onClick={() => onOpen()}

View File

@@ -112,14 +112,6 @@ export const installResource = (
let newResource = null;
if (serializedResource.kind === 'image') {
newResource = new gd.ImageResource();
} else if (serializedResource.kind === 'audio') {
newResource = new gd.AudioResource();
} else if (serializedResource.kind === 'font') {
newResource = new gd.FontResource();
} else if (serializedResource.kind === 'video') {
newResource = new gd.VideoResource();
} else if (serializedResource.kind === 'json') {
newResource = new gd.JsonResource();
} else {
throw new Error(
`Resource of kind "${serializedResource.kind}" is not supported.`
@@ -128,17 +120,13 @@ export const installResource = (
unserializeFromJSObject(newResource, serializedResource);
if (newResource.getKind() === 'image') {
// $FlowExpectedError[prop-missing] - We know the resource is an ImageResource and has the setSmooth method.
newResource.setSmooth(
project.getScaleMode() !== 'nearest' && !isPixelArt(asset)
);
}
const newName = newNameGenerator(originalResourceName, name =>
resourcesManager.hasResource(name)
);
newResource.setName(newName);
newResource.setSmooth(
project.getScaleMode() !== 'nearest' && !isPixelArt(asset)
);
newResource.setOrigin(resourceOriginCleanedName, resourceOriginIdentifier);
resourcesManager.addResource(newResource);
newResource.delete();
@@ -159,6 +147,10 @@ export const addAssetToProject = async ({
const resourceNewNames = {};
const createdObjects: Array<gdObject> = [];
asset.objectAssets.forEach(objectAsset => {
objectAsset.resources.forEach(serializedResource => {});
});
// Create objects (and their behaviors)
asset.objectAssets.forEach(objectAsset => {
const type: ?string = objectAsset.object.type;

View File

@@ -29,7 +29,6 @@ import RaisedButton from '../../UI/RaisedButton';
import { sendAssetPackBuyClicked } from '../../Utils/Analytics/EventSender';
import { MarkdownText } from '../../UI/MarkdownText';
import Paper from '../../UI/Paper';
import Window from '../../Utils/Window';
const sortedContentType = [
'sprite',
@@ -279,20 +278,6 @@ const PrivateAssetPackDialog = ({
<Trans>Redistribution &amp; reselling</Trans>
</Text>
</LineStackLayout>
<Line noMargin>
<Text>
<Link
onClick={() =>
Window.openExternalURL(
'https://gdevelop.io/page/asset-store-license-agreement'
)
}
href="https://gdevelop.io/page/asset-store-license-agreement"
>
<Trans>See details here</Trans>
</Link>
</Text>
</Line>
</Column>
</ResponsiveLineStackLayout>
</Column>

View File

@@ -20,13 +20,6 @@ const useStyles = makeStyles({
},
});
// Make dialog paper border radius match the autocomplete one.
const useStylesForPaper = makeStyles({
rounded: {
borderRadius: 4,
},
});
export type CommandPaletteInterface = {|
open: (open?: boolean) => void,
launchCommand: (commandName: CommandName) => void,
@@ -37,7 +30,6 @@ type PaletteMode = 'closed' | 'command' | 'option';
const CommandPalette = React.forwardRef<{||}, CommandPaletteInterface>(
(props, ref) => {
const classes = useStyles();
const paperClasses = useStylesForPaper();
const commandManager = React.useContext(CommandsContext);
const [mode, setMode] = React.useState<PaletteMode>('closed');
const [
@@ -105,7 +97,6 @@ const CommandPalette = React.forwardRef<{||}, CommandPaletteInterface>(
maxWidth="sm"
classes={classes}
transitionDuration={0}
PaperProps={{ classes: paperClasses }}
>
{mode === 'command' && (
// Command picker

View File

@@ -1,14 +1,9 @@
// @flow
import { t, Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import * as React from 'react';
import { ToolbarGroup } from '../UI/Toolbar';
import RaisedButton from '../UI/RaisedButton';
import FlatButton from '../UI/FlatButton';
import ProfilerIcon from '../UI/CustomSvgIcons/Profiler';
import ConsoleIcon from '../UI/CustomSvgIcons/Console';
import PlayIcon from '../UI/CustomSvgIcons/Preview';
import PauseIcon from '../UI/CustomSvgIcons/Pause';
import IconButton from '../UI/IconButton';
import ToolbarIcon from '../UI/ToolbarIcon';
import ToolbarSeparator from '../UI/ToolbarSeparator';
type Props = {|
onPlay: () => void,
@@ -36,41 +31,31 @@ export class Toolbar extends React.PureComponent<Props> {
return (
<ToolbarGroup lastChild>
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={onPlay}
src="res/ribbon_default/preview64.png"
disabled={!canPlay}
tooltip={t`Play the game`}
/>
<ToolbarIcon
onClick={onPause}
src="res/ribbon_default/pause64.png"
disabled={!canPause}
tooltip={t`Pause the game`}
/>
<ToolbarSeparator />
<ToolbarIcon
onClick={onOpenProfiler}
src="res/ribbon_default/profiler32.png"
disabled={!canOpenProfiler}
tooltip={t`Open the performance profiler`}
>
<ProfilerIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
onClick={onOpenConsole}
src="res/ribbon_default/source_cpp32.png"
disabled={!canOpenConsole}
tooltip={t`Open the console`}
>
<ConsoleIcon />
</IconButton>
{canPause ? (
<FlatButton
primary
onClick={onPause}
leftIcon={<PauseIcon />}
label={<Trans>Pause</Trans>}
/>
) : (
<RaisedButton
primary
onClick={onPlay}
icon={<PlayIcon />}
label={<Trans>Play</Trans>}
disabled={!canPlay}
/>
)}
/>
</ToolbarGroup>
);
}

View File

@@ -53,7 +53,6 @@ type State = {|
debuggerGameData: { [DebuggerId]: any },
profilerOutputs: { [DebuggerId]: ProfilerOutput },
profilingInProgress: { [DebuggerId]: boolean },
gameIsPaused: { [DebuggerId]: boolean },
selectedId: DebuggerId,
logs: { [DebuggerId]: Array<Log> },
|};
@@ -71,7 +70,6 @@ export default class Debugger extends React.Component<Props, State> {
debuggerGameData: {},
profilerOutputs: {},
profilingInProgress: {},
gameIsPaused: {},
selectedId: 0,
logs: {},
};
@@ -80,14 +78,12 @@ export default class Debugger extends React.Component<Props, State> {
_debuggerLogs: Map<number, LogsManager> = new Map();
updateToolbar() {
const { selectedId, gameIsPaused } = this.state;
this.props.setToolbar(
<Toolbar
onPlay={() => this._play(this.state.selectedId)}
onPause={() => this._pause(this.state.selectedId)}
canPlay={this._hasSelectedDebugger() && gameIsPaused[selectedId]}
canPause={this._hasSelectedDebugger() && !gameIsPaused[selectedId]}
canPlay={this._hasSelectedDebugger()}
canPause={this._hasSelectedDebugger()}
canOpenProfiler={this._hasSelectedDebugger()}
onOpenProfiler={() => {
if (this._debuggerContents[this.state.selectedId])
@@ -150,14 +146,12 @@ export default class Debugger extends React.Component<Props, State> {
debuggerGameData,
profilerOutputs,
profilingInProgress,
gameIsPaused,
}) => {
// Remove any data bound to the instance that might have been stored.
// Otherwise this would be a memory leak.
if (debuggerGameData[id]) delete debuggerGameData[id];
if (profilerOutputs[id]) delete profilerOutputs[id];
if (profilingInProgress[id]) delete profilingInProgress[id];
if (gameIsPaused[id]) delete gameIsPaused[id];
return {
debuggerIds,
@@ -170,7 +164,6 @@ export default class Debugger extends React.Component<Props, State> {
debuggerGameData,
profilerOutputs,
profilingInProgress,
gameIsPaused,
};
},
() => this.updateToolbar()
@@ -225,20 +218,6 @@ export default class Debugger extends React.Component<Props, State> {
this.setState(state => ({
profilingInProgress: { ...state.profilingInProgress, [id]: false },
}));
} else if (data.command === 'game.resumed') {
this.setState(
state => ({
gameIsPaused: { ...state.gameIsPaused, [id]: false },
}),
() => this.updateToolbar()
);
} else if (data.command === 'game.paused') {
this.setState(
state => ({
gameIsPaused: { ...state.gameIsPaused, [id]: true },
}),
() => this.updateToolbar()
);
} else if (data.command === 'hotReloader.logs') {
// Nothing to do.
} else if (data.command === 'console.log') {
@@ -256,25 +235,11 @@ export default class Debugger extends React.Component<Props, State> {
_play = (id: DebuggerId) => {
const { previewDebuggerServer } = this.props;
previewDebuggerServer.sendMessage(id, { command: 'play' });
this.setState(
state => ({
gameIsPaused: { ...state.gameIsPaused, [id]: false },
}),
() => this.updateToolbar()
);
};
_pause = (id: DebuggerId) => {
const { previewDebuggerServer } = this.props;
previewDebuggerServer.sendMessage(id, { command: 'pause' });
this.setState(
state => ({
gameIsPaused: { ...state.gameIsPaused, [id]: true },
}),
() => this.updateToolbar()
);
};
_refresh = (id: DebuggerId) => {

View File

@@ -3,6 +3,7 @@ import * as React from 'react';
import { t, Trans } from '@lingui/macro';
import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
import MoreVert from '@material-ui/icons/MoreVert';
import Add from '@material-ui/icons/Add';
import { Column, Line, Spacer } from '../UI/Grid';
@@ -39,7 +40,6 @@ import DropIndicator from '../UI/SortableVirtualizedItemList/DropIndicator';
import { ResponsiveLineStackLayout } from '../UI/Layout';
import Text from '../UI/Text';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
import ThreeDotsMenu from '../UI/CustomSvgIcons/ThreeDotsMenu';
const DragSourceAndDropTarget = makeDragSourceAndDropTarget('effects-list');
@@ -325,7 +325,7 @@ export default function EffectsList(props: Props) {
<ElementWithMenu
element={
<IconButton size="small">
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -12,6 +12,7 @@ import RaisedButton from '../UI/RaisedButton';
import IconButton from '../UI/IconButton';
import EmptyMessage from '../UI/EmptyMessage';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import MoreVert from '@material-ui/icons/MoreVert';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import MiniToolbar from '../UI/MiniToolbar';
import { showWarningBox } from '../UI/Messages/MessageBox';
@@ -26,7 +27,6 @@ import ColorField from '../UI/ColorField';
import BehaviorTypeSelector from '../BehaviorTypeSelector';
import SemiControlledAutoComplete from '../UI/SemiControlledAutoComplete';
import ScrollView from '../UI/ScrollView';
import ThreeDotsMenu from '../UI/CustomSvgIcons/ThreeDotsMenu';
const gd: libGDevelop = global.gd;
@@ -208,7 +208,7 @@ export default class EventsBasedBehaviorPropertiesEditor extends React.Component
<ElementWithMenu
element={
<IconButton>
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -12,6 +12,7 @@ import RaisedButton from '../UI/RaisedButton';
import IconButton from '../UI/IconButton';
import EmptyMessage from '../UI/EmptyMessage';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import MoreVert from '@material-ui/icons/MoreVert';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import MiniToolbar from '../UI/MiniToolbar';
import { showWarningBox } from '../UI/Messages/MessageBox';
@@ -24,7 +25,6 @@ import { ResponsiveLineStackLayout, ColumnStackLayout } from '../UI/Layout';
import StringArrayEditor from '../StringArrayEditor';
import ColorField from '../UI/ColorField';
import SemiControlledAutoComplete from '../UI/SemiControlledAutoComplete';
import ThreeDotsMenu from '../UI/CustomSvgIcons/ThreeDotsMenu';
const gd: libGDevelop = global.gd;
@@ -212,7 +212,7 @@ export default class EventsBasedObjectPropertiesEditor extends React.Component<
<ElementWithMenu
element={
<IconButton>
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -10,6 +10,7 @@ import RaisedButton from '../../UI/RaisedButton';
import IconButton from '../../UI/IconButton';
import EmptyMessage from '../../UI/EmptyMessage';
import ElementWithMenu from '../../UI/Menu/ElementWithMenu';
import MoreVert from '@material-ui/icons/MoreVert';
import HelpButton from '../../UI/HelpButton';
import SemiControlledTextField from '../../UI/SemiControlledTextField';
import MiniToolbar, { MiniToolbarText } from '../../UI/MiniToolbar';
@@ -25,7 +26,6 @@ import { ColumnStackLayout } from '../../UI/Layout';
import { getLastObjectParameterObjectType } from '../../EventsSheet/ParameterFields/ParameterMetadataTools';
import newNameGenerator from '../../Utils/NewNameGenerator';
import ValueTypeEditor from './ValueTypeEditor';
import ThreeDotsMenu from '../../UI/CustomSvgIcons/ThreeDotsMenu';
const gd: libGDevelop = global.gd;
@@ -352,7 +352,7 @@ export default class EventsFunctionParametersEditor extends React.Component<
<ElementWithMenu
element={
<IconButton>
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -9,7 +9,6 @@ import {
shouldFocusNextField,
shouldFocusPreviousField,
shouldSubmit,
shouldValidate,
} from '../UI/KeyboardShortcuts/InteractionKeys';
const styles = {
@@ -93,13 +92,6 @@ export default function InlinePopover(props: Props) {
if (shouldCloseOrCancel(event)) {
props.onRequestClose();
} else if (shouldSubmit(event)) {
props.onApply();
} else if (shouldValidate(event)) {
// Stop propagation to avoid immediately re-opening the inline popover (as the key down
// would be detected by the parameter of the instruction).
event.stopPropagation();
event.preventDefault();
props.onApply();
}

View File

@@ -87,7 +87,7 @@ const getInitialTab = (
* A responsive instruction editor in a dialog, showing InstructionParametersEditor
* at the end.
*/
export default function InstructionEditorDialog({
export default function NewInstructionEditorDialog({
project,
globalObjectsContainer,
objectsContainer,

View File

@@ -21,10 +21,7 @@ import EmptyMessage from '../../../UI/EmptyMessage';
import ScrollView, { type ScrollViewInterface } from '../../../UI/ScrollView';
import { Line } from '../../../UI/Grid';
import RaisedButton from '../../../UI/RaisedButton';
import {
getInstructionListItemValue,
getInstructionOrExpressionIdentifier,
} from '../SelectorListItems/Keys';
import { getInstructionListItemValue } from '../SelectorListItems/Keys';
import { ResponsiveLineStackLayout } from '../../../UI/Layout';
import {
tuneMatches,
@@ -174,9 +171,12 @@ export default class InstructionOrExpressionSelector<
}) =>
renderInstructionOrExpressionListItem({
instructionOrExpressionMetadata: enumeratedInstructionOrExpressionMetadata,
id: getInstructionOrExpressionIdentifier(
enumeratedInstructionOrExpressionMetadata
),
id:
'instruction-or-expression-' +
enumeratedInstructionOrExpressionMetadata.type.replace(
/:/g,
'-'
),
iconSize: iconSize,
onClick: () =>
onChoose(

View File

@@ -23,26 +23,9 @@ export const getObjectOrObjectGroupListItemValue = (
export const getInstructionListItemKey = (
instruction: EnumeratedInstructionOrExpressionMetadata
) =>
`instruction-key-${instruction.fullGroupName}${
instruction.scope.objectMetadata
? '-' + instruction.scope.objectMetadata.getName()
: ''
}-${instruction.type}`;
) => `instruction-key-${instruction.fullGroupName}-${instruction.type}`;
export const getInstructionListItemValue = (instructionType: string) =>
`instruction-value-${instructionType}`;
export const getSubheaderListItemKey = (subheader: string) =>
`subheader-key-${subheader}`;
export const getInstructionOrExpressionIdentifier = (
instructionOrExpressionMetadata: EnumeratedInstructionOrExpressionMetadata
): string =>
`instruction-or-expression-${
instructionOrExpressionMetadata.scope.objectMetadata
? instructionOrExpressionMetadata.scope.objectMetadata
.getName()
.replace(/:/g, '-') + '-'
: ''
}${instructionOrExpressionMetadata.type.replace(/:/g, '-')}`;

View File

@@ -57,9 +57,6 @@ export const renderInstructionOrExpressionTree = <
primaryText={key}
selected={selected}
id={
// TODO: This id is used by in app tutorials. When in app tutorials
// are linked to GDevelop versions, change this id to be more accurate
// using getInstructionOrExpressionIdentifier
'instruction-item-' +
instructionInformation.type.replace(/:/g, '-')
}

View File

@@ -136,10 +136,6 @@ export const handleAutocompletionsKeyDown = (
// the user should be able to freely move to the next line.
if (autocompletion.isExact) return state;
onInsertAutocompletion(autocompletion);
// Stop propagation to avoid closing the modal the
// field is contained in.
event.stopPropagation();
}
// Avoid entering a new line or tabbing to the next field.

View File

@@ -40,7 +40,6 @@ import { ResponsiveWindowMeasurer } from '../../../UI/Reponsive/ResponsiveWindow
import {
shouldCloseOrCancel,
shouldSubmit,
shouldValidate,
} from '../../../UI/KeyboardShortcuts/InteractionKeys';
import Paper from '../../../UI/Paper';
const gd: libGDevelop = global.gd;
@@ -583,11 +582,7 @@ export default class ExpressionField extends React.Component<Props, State> {
// Apply the changes now as otherwise the onBlur handler
// has a risk not to be called (as the component will be
// unmounted).
if (
shouldCloseOrCancel(event) ||
shouldSubmit(event) ||
shouldValidate(event)
) {
if (shouldCloseOrCancel(event) || shouldSubmit(event)) {
const value = event.currentTarget.value;
if (this.props.onChange) this.props.onChange(value);
}

View File

@@ -4,7 +4,7 @@ import { Trans, t } from '@lingui/macro';
import * as React from 'react';
import Background from '../UI/Background';
import TextField from '../UI/TextField';
import { Column, Line } from '../UI/Grid';
import { Column, Line, Spacer } from '../UI/Grid';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import IconButton from '../UI/IconButton';
@@ -16,11 +16,7 @@ import {
type ReplaceInEventsInputs,
} from './EventsSearcher';
import RaisedButton from '../UI/RaisedButton';
import {
ColumnStackLayout,
LineStackLayout,
ResponsiveLineStackLayout,
} from '../UI/Layout';
import { ColumnStackLayout } from '../UI/Layout';
import {
shouldBrowsePrevious,
shouldCloseOrCancel,
@@ -166,9 +162,9 @@ const SearchPanel = (
return (
<Background noFullHeight noExpand>
<Column noMargin>
<Column>
<Line>
<Column expand noOverflowParent>
<Column expand>
<Tabs
value={currentTab}
onChange={setCurrentTab}
@@ -189,7 +185,7 @@ const SearchPanel = (
</Line>
<Line noMargin>
<ColumnStackLayout expand>
<LineStackLayout alignItems="baseline" noMargin>
<Line alignItems="baseline" noMargin>
<TextField
ref={searchTextField}
type="search"
@@ -221,6 +217,7 @@ const SearchPanel = (
value={searchText}
fullWidth
/>
<Spacer />
<RaisedButton
disabled={shouldDisableSearch}
primary
@@ -233,9 +230,9 @@ const SearchPanel = (
}
}}
/>
</LineStackLayout>
</Line>
{isSearchAndReplaceTab() && (
<LineStackLayout alignItems="baseline" noMargin>
<Line alignItems="baseline" noMargin>
<TextField
type="search"
margin="dense"
@@ -256,64 +253,56 @@ const SearchPanel = (
value={replaceText}
fullWidth
/>
<Spacer />
<RaisedButton
disabled={shouldDisableReplace}
label={<Trans>Replace</Trans>}
onClick={launchReplace}
/>
</LineStackLayout>
</Line>
)}
<ResponsiveLineStackLayout
noMargin
alignItems="center"
justifyContent="space-between"
>
<ResponsiveLineStackLayout noMargin alignItems="center">
<LineStackLayout noMargin alignItems="center">
<InlineCheckbox
label={<Trans>Case insensitive</Trans>}
checked={!matchCase}
onCheck={(e, checked) => {
setMatchCase(!checked);
}}
/>
{windowWidth !== 'small' && (
<Text>
<Trans>Search in:</Trans>
</Text>
)}
<InlineCheckbox
label={<Trans>Conditions</Trans>}
checked={searchInConditions}
onCheck={(e, checked) => {
setSearchInConditions(checked);
}}
/>
</LineStackLayout>
<Line noMargin alignItems="center">
<InlineCheckbox
label={<Trans>Actions</Trans>}
checked={searchInActions}
onCheck={(e, checked) => {
setSearchInActions(checked);
}}
/>
<InlineCheckbox
label={<Trans>Texts</Trans>}
checked={searchInEventStrings}
onCheck={(e, checked) => {
setSearchInEventStrings(checked);
}}
/>
{/* <InlineCheckbox //TODO: Implement search/replace in selection
<Line noMargin alignItems="center" justifyContent="space-between">
<Line noMargin alignItems="center">
<InlineCheckbox
label={<Trans>Case insensitive</Trans>}
checked={!matchCase}
onCheck={(e, checked) => {
setMatchCase(!checked);
}}
/>
<Text>
<Trans>Search in:</Trans>
</Text>
<Spacer />
<InlineCheckbox
label={<Trans>Conditions</Trans>}
checked={searchInConditions}
onCheck={(e, checked) => {
setSearchInConditions(checked);
}}
/>
<InlineCheckbox
label={<Trans>Actions</Trans>}
checked={searchInActions}
onCheck={(e, checked) => {
setSearchInActions(checked);
}}
/>
<InlineCheckbox
label={<Trans>Texts</Trans>}
checked={searchInEventStrings}
onCheck={(e, checked) => {
setSearchInEventStrings(checked);
}}
/>
{/* <InlineCheckbox //TODO: Implement search/replace in selection
label={<Trans>Replace in selection</Trans>}
checked={searchInSelection}
onCheck={(e, checked) =>
this.setState({ searchInSelection: checked })}
/> */}
</Line>
</ResponsiveLineStackLayout>
<Line noMargin alignItems="center" justifyContent="flex-end">
</Line>
<Line noMargin alignItems="center">
<Text>
{resultsCount === null || resultsCount === undefined ? (
''
@@ -353,7 +342,7 @@ const SearchPanel = (
}}
/>
</Line>
</ResponsiveLineStackLayout>
</Line>
</ColumnStackLayout>
</Line>
</Column>

View File

@@ -3,19 +3,10 @@ import { t } from '@lingui/macro';
import React, { PureComponent } from 'react';
import { ToolbarGroup } from '../UI/Toolbar';
import ToolbarSeparator from '../UI/ToolbarSeparator';
import IconButton from '../UI/IconButton';
import ToolbarIcon from '../UI/ToolbarIcon';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import ToolbarCommands from './ToolbarCommands';
import { type EventMetadata } from './EnumerateEventsMetadata';
import AddEventIcon from '../UI/CustomSvgIcons/AddEvent';
import AddSubEventIcon from '../UI/CustomSvgIcons/AddSubEvent';
import AddCommentIcon from '../UI/CustomSvgIcons/AddComment';
import CircledAddIcon from '../UI/CustomSvgIcons/CircledAdd';
import TrashIcon from '../UI/CustomSvgIcons/Trash';
import UndoIcon from '../UI/CustomSvgIcons/Undo';
import RedoIcon from '../UI/CustomSvgIcons/Redo';
import ToolbarSearchIcon from '../UI/CustomSvgIcons/ToolbarSearch';
import EditSceneIcon from '../UI/CustomSvgIcons/EditScene';
type Props = {|
onAddStandardEvent: () => void,
@@ -44,45 +35,31 @@ export class Toolbar extends PureComponent<Props> {
<>
<ToolbarCommands {...this.props} />
<ToolbarGroup lastChild>
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={this.props.onAddStandardEvent}
src="res/ribbon_default/eventadd32.png"
id="toolbar-add-event-button"
tooltip={t`Add a new empty event`}
>
<AddEventIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
onClick={this.props.onAddSubEvent}
src="res/ribbon_default/subeventadd32.png"
disabled={!this.props.canAddSubEvent}
id="toolbar-add-sub-event-button"
tooltip={t`Add a sub-event to the selected event`}
>
<AddSubEventIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
onClick={this.props.onAddCommentEvent}
src="res/ribbon_default/commentaireadd32.png"
id="toolbar-add-comment-button"
tooltip={t`Add a comment`}
>
<AddCommentIcon />
</IconButton>
/>
<ElementWithMenu
element={
<IconButton
size="small"
color="default"
<ToolbarIcon
src="res/ribbon_default/add32.png"
tooltip={t`Choose and add an event`}
>
<CircledAddIcon />
</IconButton>
/>
}
buildMenuTemplate={() =>
this.props.allEventsMetadata.map(metadata => {
@@ -96,57 +73,38 @@ export class Toolbar extends PureComponent<Props> {
}
/>
<ToolbarSeparator />
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={this.props.onRemove}
src="res/ribbon_default/deleteselected32.png"
disabled={!this.props.canRemove}
tooltip={t`Delete the selected event(s)`}
>
<TrashIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
onClick={this.props.undo}
src="res/ribbon_default/undo32.png"
disabled={!this.props.canUndo}
tooltip={t`Undo the last changes`}
>
<UndoIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
onClick={this.props.redo}
src="res/ribbon_default/redo32.png"
disabled={!this.props.canRedo}
tooltip={t`Redo the last changes`}
>
<RedoIcon />
</IconButton>
/>
<ToolbarSeparator />
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={() => this.props.onToggleSearchPanel()}
src="res/ribbon_default/search32.png"
tooltip={t`Search in events`}
acceleratorString={'CmdOrCtrl+F'}
>
<ToolbarSearchIcon />
</IconButton>
/>
{this.props.onOpenSettings && <ToolbarSeparator />}
{this.props.onOpenSettings && (
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={this.props.onOpenSettings}
src="res/ribbon_default/pref32.png"
tooltip={t`Open settings`}
>
<EditSceneIcon />
</IconButton>
/>
)}
</ToolbarGroup>
</>

View File

@@ -7,6 +7,7 @@ import { differenceInCalendarDays, format } from 'date-fns';
import PhoneIphone from '@material-ui/icons/PhoneIphone';
import LaptopMac from '@material-ui/icons/LaptopMac';
import MoreVert from '@material-ui/icons/MoreVert';
import { Line, LargeSpacer, Spacer, Column } from '../../UI/Grid';
import EmptyMessage from '../../UI/EmptyMessage';
@@ -37,7 +38,6 @@ import { shortenUuidForDisplay } from '../../Utils/GDevelopServices/Play';
import { type AuthenticatedUser } from '../../Profile/AuthenticatedUserContext';
import Window from '../../Utils/Window';
import CircularProgress from '../../UI/CircularProgress';
import ThreeDotsMenu from '../../UI/CustomSvgIcons/ThreeDotsMenu';
const styles = {
icon: {
@@ -215,7 +215,7 @@ export const BuildCard = ({
<ElementWithMenu
element={
<IconButton size="small" disabled={gameUpdating}>
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -7,6 +7,7 @@ import { type I18n as I18nType } from '@lingui/core';
import { Chip } from '@material-ui/core';
import CardHeader from '@material-ui/core/CardHeader';
import ShareIcon from '@material-ui/icons/Share';
import MoreVert from '@material-ui/icons/MoreVert';
import { Column, Line, Spacer } from '../UI/Grid';
import RaisedButton from '../UI/RaisedButton';
@@ -32,7 +33,6 @@ import { type GameDetailsTab } from './GameDetailsDialog';
import { showErrorBox } from '../UI/Messages/MessageBox';
import BackgroundText from '../UI/BackgroundText';
import Card from '../UI/Card';
import ThreeDotsMenu from '../UI/CustomSvgIcons/ThreeDotsMenu';
type Props = {|
game: Game,
@@ -189,7 +189,7 @@ export const GameCard = ({
<ElementWithMenu
element={
<IconButton size="small" disabled={isDeletingGame}>
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -1,80 +0,0 @@
// @flow
import * as React from 'react';
import { Trans } from '@lingui/macro';
import Text from '../../UI/Text';
import { Column, Line } from '../../UI/Grid';
import { LineStackLayout } from '../../UI/Layout';
import Link from '../../UI/Link';
import { SubscriptionSuggestionContext } from '../../Profile/Subscription/SubscriptionSuggestionContext';
import GDevelopThemeContext from '../../UI/Theme/ThemeContext';
import Window from '../../Utils/Window';
const styles = {
subscriptionContainer: {
display: 'flex',
borderRadius: 10,
alignItems: 'center',
},
diamondIcon: {
width: 50,
height: 50,
},
};
const GetSubscriptionCard = () => {
const { openSubscriptionDialog } = React.useContext(
SubscriptionSuggestionContext
);
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const subscriptionContainerStyle = {
...styles.subscriptionContainer,
border: `1px solid ${gdevelopTheme.palette.primary}`,
};
return (
<div style={subscriptionContainerStyle}>
<img src="res/diamond.svg" style={styles.diamondIcon} alt="diamond" />
<LineStackLayout alignItems="center">
<Column noMargin>
<Text>
<Trans>
Get a silver or gold subscription to unlock color customization.
</Trans>
</Text>
<Line noMargin>
<Link
href="https://liluo.io/playground/test-leaderboard"
onClick={() =>
Window.openExternalURL(
'https://liluo.io/playground/test-leaderboard'
)
}
>
<Text noMargin color="inherit">
<Trans>Test it out!</Trans>
</Text>
</Link>
</Line>
</Column>
<Column>
<Link
href="#"
onClick={() => {
openSubscriptionDialog({
reason: 'Leaderboard customization',
});
}}
>
<Text noMargin color="inherit">
<Trans>Upgrade</Trans>
</Text>
</Link>
</Column>
</LineStackLayout>
</div>
);
};
export default GetSubscriptionCard;

View File

@@ -6,19 +6,17 @@ import { type I18n as I18nType } from '@lingui/core';
import Dialog, { DialogPrimaryButton } from '../../UI/Dialog';
import FlatButton from '../../UI/FlatButton';
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../../UI/Layout';
import { ResponsiveLineStackLayout } from '../../UI/Layout';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import Text from '../../UI/Text';
import TextField from '../../UI/TextField';
import {
canUserCustomizeLeaderboardTheme,
getRGBLeaderboardTheme,
type LeaderboardCustomizationSettings,
type LeaderboardScoreFormattingTimeUnit,
} from '../../Utils/GDevelopServices/Play';
import { Spacer } from '../../UI/Grid';
import { Column, Line, Spacer } from '../../UI/Grid';
import {
formatScore,
orderedTimeUnits,
@@ -26,11 +24,6 @@ import {
} from '../../Leaderboard/LeaderboardScoreFormatter';
import AlertMessage from '../../UI/AlertMessage';
import HelpButton from '../../UI/HelpButton';
import ColorField from '../../UI/ColorField';
import AuthenticatedUserContext from '../../Profile/AuthenticatedUserContext';
import GetSubscriptionCard from './GetSubscriptionCard';
import LeaderboardPlaygroundCard from './LeaderboardPlaygroundCard';
import { rgbStringToHexString } from '../../Utils/ColorTransformer';
const unitToAbbreviation = {
hour: 'HH',
@@ -96,13 +89,6 @@ function LeaderboardAppearanceDialog({
leaderboardCustomizationSettings,
}: Props) {
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const authenticatedUser = React.useContext(AuthenticatedUserContext);
const canUserCustomizeTheme = canUserCustomizeLeaderboardTheme(
authenticatedUser
);
const rgbLeaderboardTheme = getRGBLeaderboardTheme(
leaderboardCustomizationSettings
);
const [scoreTitleError, setScoreTitleError] = React.useState<?string>(null);
const [
defaultDisplayedEntriesNumber,
@@ -112,19 +98,6 @@ function LeaderboardAppearanceDialog({
leaderboardCustomizationSettings.defaultDisplayedEntriesNumber) ||
20
);
const [backgroundColor, setBackgroundColor] = React.useState<string>(
rgbLeaderboardTheme.backgroundColor
);
const [textColor, setTextColor] = React.useState<string>(
rgbLeaderboardTheme.textColor
);
const [
highlightBackgroundColor,
setHighlightBackgroundColor,
] = React.useState<string>(rgbLeaderboardTheme.highlightBackgroundColor);
const [highlightTextColor, setHighlightTextColor] = React.useState<string>(
rgbLeaderboardTheme.highlightTextColor
);
const [
defaultDisplayedEntriesNumberError,
setDefaultDisplayedEntriesNumberError,
@@ -196,7 +169,7 @@ function LeaderboardAppearanceDialog({
return;
}
setIsLoading(true);
const customizationSettings: LeaderboardCustomizationSettings = {
const customizationSettings = {
defaultDisplayedEntriesNumber,
scoreTitle,
scoreFormatting:
@@ -208,16 +181,6 @@ function LeaderboardAppearanceDialog({
precision,
}
: { type: scoreType, ...unitSelectOptions[timeUnits] },
theme: canUserCustomizeTheme
? {
backgroundColor: rgbStringToHexString(backgroundColor),
textColor: rgbStringToHexString(textColor),
highlightBackgroundColor: rgbStringToHexString(
highlightBackgroundColor
),
highlightTextColor: rgbStringToHexString(highlightTextColor),
}
: undefined,
};
await onSave(customizationSettings);
};
@@ -257,10 +220,10 @@ function LeaderboardAppearanceDialog({
onSaveSettings(i18n);
}}
>
<ColumnStackLayout noMargin>
<Text size="sub-title" noMargin>
<Trans>Table settings</Trans>
</Text>
<Text size="block-title">
<Trans>Table settings</Trans>
</Text>
<Line>
<TextField
fullWidth
type="number"
@@ -285,53 +248,12 @@ function LeaderboardAppearanceDialog({
)
);
}}
disabled={isLoading}
/>
<ResponsiveLineStackLayout noMargin>
<ColorField
floatingLabelText={<Trans>Background color</Trans>}
disableAlpha
fullWidth
color={backgroundColor}
onChange={setBackgroundColor}
disabled={!canUserCustomizeTheme || isLoading}
/>
<ColorField
floatingLabelText={<Trans>Text color</Trans>}
disableAlpha
fullWidth
color={textColor}
onChange={setTextColor}
disabled={!canUserCustomizeTheme || isLoading}
/>
</ResponsiveLineStackLayout>
<ResponsiveLineStackLayout noMargin>
<ColorField
floatingLabelText={<Trans>Highlight background color</Trans>}
disableAlpha
fullWidth
color={highlightBackgroundColor}
onChange={setHighlightBackgroundColor}
disabled={!canUserCustomizeTheme || isLoading}
/>
<ColorField
floatingLabelText={<Trans>Highlight text color</Trans>}
disableAlpha
fullWidth
color={highlightTextColor}
onChange={setHighlightTextColor}
disabled={!canUserCustomizeTheme || isLoading}
/>
</ResponsiveLineStackLayout>
{!canUserCustomizeTheme ? (
<GetSubscriptionCard />
) : (
<LeaderboardPlaygroundCard />
)}
<Spacer />
<Text size="sub-title" noMargin>
<Trans>Score column settings</Trans>
</Text>
</Line>
<Text size="block-title">
<Trans>Score column settings</Trans>
</Text>
<Line>
<TextField
fullWidth
floatingLabelText={<Trans>Column title</Trans>}
@@ -342,157 +264,179 @@ function LeaderboardAppearanceDialog({
if (!!scoreTitleError && !!newTitle) setScoreTitleError(null);
setScoreTitle(newTitle);
}}
disabled={isLoading}
/>
<SelectField
fullWidth
value={scoreType}
floatingLabelText={<Trans>Score display</Trans>}
onChange={(e, i, newValue) =>
// $FlowIgnore
setScoreType(newValue)
}
disabled={isLoading}
>
<SelectOption
key={'custom'}
value={'custom'}
primaryText={t`Custom display`}
/>
<SelectOption
key={'time'}
value={'time'}
primaryText={t`Display as time`}
/>
</SelectField>
<Spacer />
<Text size="sub-title" noMargin>
<Trans>Settings</Trans>
</Text>
{scoreType === 'custom' ? (
<ColumnStackLayout noMargin>
<ResponsiveLineStackLayout noMargin>
<TextField
fullWidth
floatingLabelFixed
floatingLabelText={<Trans>Prefix</Trans>}
maxLength={10}
value={prefix}
translatableHintText={t`Ex: $`}
onChange={(e, newValue) => {
setPrefix(newValue);
}}
disabled={isLoading}
/>
<TextField
fullWidth
floatingLabelFixed
floatingLabelText={<Trans>Suffix</Trans>}
maxLength={10}
value={suffix}
translatableHintText={t`Ex: coins`}
onChange={(e, newValue) => {
setSuffix(newValue);
}}
disabled={isLoading}
/>
</ResponsiveLineStackLayout>
</Line>
<Column noMargin>
<Line>
<SelectField
fullWidth
value={scoreType}
floatingLabelText={<Trans>Score display</Trans>}
onChange={(e, i, newValue) =>
// $FlowIgnore
setScoreType(newValue)
}
>
<SelectOption
key={'custom'}
value={'custom'}
primaryText={t`Custom display`}
/>
<SelectOption
key={'time'}
value={'time'}
primaryText={t`Display as time`}
/>
</SelectField>
</Line>
<Column>
<Line noMargin>
<Text size="body2">
<Trans>Settings</Trans>
</Text>
</Line>
{scoreType === 'custom' ? (
<>
<ResponsiveLineStackLayout noColumnMargin>
<Column expand noMargin>
<TextField
fullWidth
floatingLabelFixed
floatingLabelText={<Trans>Prefix</Trans>}
maxLength={10}
value={prefix}
translatableHintText={t`Ex: $`}
onChange={(e, newValue) => {
setPrefix(newValue);
}}
/>
</Column>
<Column expand noMargin>
<TextField
fullWidth
floatingLabelFixed
floatingLabelText={<Trans>Suffix</Trans>}
maxLength={10}
value={suffix}
translatableHintText={t`Ex: coins`}
onChange={(e, newValue) => {
setSuffix(newValue);
}}
/>
</Column>
</ResponsiveLineStackLayout>
<Spacer />
<ResponsiveLineStackLayout noColumnMargin noMargin>
<Column expand noMargin>
<TextField
fullWidth
type="number"
floatingLabelText={
<Trans>Round to X decimal point</Trans>
}
errorText={precisionError}
value={isNaN(precision) ? '' : precision}
min={precisionMinValue}
max={precisionMaxValue}
onChange={(e, newValue) => {
if (!!precisionError && !!newValue) {
setPrecisionError(null);
}
setPrecision(
Math.max(
precisionMinValue,
Math.min(precisionMaxValue, parseFloat(newValue))
)
);
}}
/>
</Column>
<Column expand noMargin />
</ResponsiveLineStackLayout>
</>
) : (
<>
<Line noMargin>
<SelectField
fullWidth
value={timeUnits}
floatingLabelText={<Trans>Time format</Trans>}
onChange={(e, i, newValue) =>
// $FlowIgnore
setTimeUnits(newValue)
}
>
{Object.keys(unitSelectOptions).map(option => (
<SelectOption
key={option}
value={option}
primaryText={option}
/>
))}
</SelectField>
</Line>
<Line>
<AlertMessage kind="info">
<Trans>
To use this formatting, you must send a score expressed
in seconds
</Trans>
</AlertMessage>
</Line>
</>
)}
<Spacer />
<Line noMargin>
<Text size="body2">
<Trans>Preview</Trans>
</Text>
</Line>
<ResponsiveLineStackLayout noColumnMargin>
<TextField
fullWidth
type="number"
floatingLabelText={<Trans>Round to X decimal point</Trans>}
errorText={precisionError}
value={isNaN(precision) ? '' : precision}
min={precisionMinValue}
max={precisionMaxValue}
onChange={(e, newValue) => {
if (!!precisionError && !!newValue) {
setPrecisionError(null);
}
setPrecision(
Math.max(
precisionMinValue,
Math.min(precisionMaxValue, parseFloat(newValue))
)
);
}}
disabled={isLoading}
/>
</ColumnStackLayout>
) : (
<ColumnStackLayout noMargin>
<SelectField
fullWidth
value={timeUnits}
floatingLabelText={<Trans>Time format</Trans>}
onChange={(e, i, newValue) => setTimeUnits(newValue)}
disabled={isLoading}
>
{Object.keys(unitSelectOptions).map(option => (
<SelectOption
key={option}
value={option}
primaryText={option}
/>
))}
</SelectField>
<AlertMessage kind="info">
<Trans>
To use this formatting, you must send a score expressed in
seconds
</Trans>
</AlertMessage>
</ColumnStackLayout>
)}
<Spacer />
<Text size="sub-title" noMargin>
<Trans>Preview</Trans>
</Text>
<ResponsiveLineStackLayout noMargin>
<TextField
fullWidth
floatingLabelText={
scoreType === 'custom' ? (
<Trans>Test value</Trans>
) : (
<Trans>Test value (in second)</Trans>
)
}
max={scorePreviewMaxValue}
min={0}
type="number"
value={isNaN(scorePreview) ? '' : scorePreview}
onChange={(e, value) =>
setScorePreview(
Math.max(
0,
Math.min(scorePreviewMaxValue, parseFloat(value))
floatingLabelText={
scoreType === 'custom' ? (
<Trans>Test value</Trans>
) : (
<Trans>Test value (in second)</Trans>
)
)
}
disabled={isLoading}
/>
<TextField
disabled
fullWidth
floatingLabelText={<Trans>Displayed score</Trans>}
value={formatScore(
scorePreview || 0,
scoreType === 'time'
? {
type: scoreType,
...unitSelectOptions[timeUnits],
}
: {
type: scoreType,
prefix,
suffix,
precision: precision || 0,
}
)}
/>
</ResponsiveLineStackLayout>
</ColumnStackLayout>
}
max={scorePreviewMaxValue}
min={0}
type="number"
value={isNaN(scorePreview) ? '' : scorePreview}
onChange={(e, value) =>
setScorePreview(
Math.max(
0,
Math.min(scorePreviewMaxValue, parseFloat(value))
)
)
}
/>
<TextField
disabled
fullWidth
floatingLabelText={<Trans>Displayed score</Trans>}
value={formatScore(
scorePreview || 0,
scoreType === 'time'
? {
type: scoreType,
...unitSelectOptions[timeUnits],
}
: {
type: scoreType,
prefix,
suffix,
precision: precision || 0,
}
)}
/>
</ResponsiveLineStackLayout>
</Column>
</Column>
</Dialog>
)}
</I18n>

View File

@@ -1,44 +0,0 @@
// @flow
import * as React from 'react';
import { Trans } from '@lingui/macro';
import Text from '../../UI/Text';
import { Column } from '../../UI/Grid';
import { LineStackLayout } from '../../UI/Layout';
import Link from '../../UI/Link';
import Window from '../../Utils/Window';
const LeaderboardPlaygroundCard = () => {
return (
<LineStackLayout
expand
noMargin
alignItems="center"
justifyContent="space-around"
>
<Column>
<Text>
<Trans>
Experiment with the leaderboard colors using the playground
</Trans>
</Text>
</Column>
<Column>
<Link
href="https://liluo.io/playground/test-leaderboard"
onClick={() =>
Window.openExternalURL(
'https://liluo.io/playground/test-leaderboard'
)
}
>
<Text noMargin color="inherit">
<Trans>Playground</Trans>
</Text>
</Link>
</Column>
</LineStackLayout>
);
};
export default LeaderboardPlaygroundCard;

View File

@@ -277,7 +277,7 @@ export const LeaderboardAdmin = ({
const onListLeaderboards = React.useCallback(
() => {
const fetchAndHandleError = async () => {
setIsRequestPending(true); // We only set the local loading state here to avoid blocking the dialog buttons on first load.
setIsLoading(true);
setApiError(null);
try {
await listLeaderboards();
@@ -297,12 +297,12 @@ export const LeaderboardAdmin = ({
),
});
} finally {
setIsRequestPending(false);
setIsLoading(false);
}
};
fetchAndHandleError();
},
[listLeaderboards]
[setIsLoading, listLeaderboards]
);
const onFetchLeaderboardEntries = async () => {
@@ -367,7 +367,6 @@ export const LeaderboardAdmin = ({
const answer = await showConfirmation({
title: t`Reset leaderboard ${currentLeaderboard.name}`,
message: t`All current entries will be deleted, are you sure you want to reset this leaderboard? This can't be undone.`,
confirmButtonLabel: t`Reset leaderboard`,
});
if (!answer) return;
@@ -402,7 +401,6 @@ export const LeaderboardAdmin = ({
const answer = await showDeleteConfirmation({
title: t`Delete leaderboard ${currentLeaderboard.name}`,
message: t`Are you sure you want to delete this leaderboard and all of its entries? This can't be undone.`,
confirmButtonLabel: t`Delete Leaderboard`,
confirmText: currentLeaderboard.name,
fieldMessage: t`Type the name of the leaderboard:`,
});
@@ -436,7 +434,6 @@ export const LeaderboardAdmin = ({
const answer = await showConfirmation({
title: t`Delete score ${entry.score} from ${entry.playerName}`,
message: t`Are you sure you want to delete this entry? This can't be undone.`,
confirmButtonLabel: t`Delete Entry`,
});
if (!answer) return;

View File

@@ -13,12 +13,12 @@ import Delete from '@material-ui/icons/Delete';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import DragHandle from '../UI/DragHandle';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import MoreVert from '@material-ui/icons/MoreVert';
import EmojiObjectsIcon from '@material-ui/icons/EmojiObjects';
import EditIcon from '@material-ui/icons/Edit';
import Badge from '../UI/Badge';
import { makeDragSourceAndDropTarget } from '../UI/DragAndDrop/DragSourceAndDropTarget';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
import ThreeDotsMenu from '../UI/CustomSvgIcons/ThreeDotsMenu';
const DragSourceAndDropTarget = makeDragSourceAndDropTarget('layers-list');
@@ -115,7 +115,7 @@ const LayerRow = ({
<ElementWithMenu
element={
<IconButton size="small">
<ThreeDotsMenu />
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [

View File

@@ -58,9 +58,7 @@ export const create = (authentication: Authentication) => {
storageProviders={[
LocalFileStorageProvider,
UrlStorageProvider,
supportsCloudProjects
? CloudStorageProvider
: FakeCloudStorageProvider,
supportsCloudProjects ? CloudStorageProvider : FakeCloudStorageProvider,
]}
defaultStorageProvider={LocalFileStorageProvider}
>
@@ -72,9 +70,7 @@ export const create = (authentication: Authentication) => {
}) => (
<MainFrame
i18n={i18n}
renderMainMenu={(props, callbacks) => (
<ElectronMainMenu props={props} callbacks={callbacks} />
)}
renderMainMenu={props => <ElectronMainMenu {...props} />}
renderPreviewLauncher={(props, ref) => (
<LocalPreviewLauncher {...props} ref={ref} />
)}

View File

@@ -50,8 +50,6 @@ import ImageTileRow from '../../../../UI/ImageTileRow';
import { prepareExamples } from '../../../../AssetStore/ExampleStore';
import Window from '../../../../Utils/Window';
import Skeleton from '@material-ui/lab/Skeleton';
import BackgroundText from '../../../../UI/BackgroundText';
import Paper from '../../../../UI/Paper';
const electron = optionalRequire('electron');
const path = optionalRequire('path');
@@ -64,7 +62,6 @@ const styles = {
overflowWrap: 'anywhere', // Ensure everything is wrapped on small devices.
},
projectSkeleton: { borderRadius: 6 },
noProjectsContainer: { padding: 10 },
};
const getTemplatesGridSizeFromWidth = (width: WidthType) => {
@@ -225,7 +222,6 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
message: t`Youre about to permanently delete your project ${projectName}. You will no longer be able to access it.`,
fieldMessage: t`Type your project name to delete it:`,
confirmText: projectName,
confirmButtonLabel: t`Delete project`,
});
if (!deleteAnswer) return;
@@ -409,172 +405,155 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
</LineStackLayout>
)}
<List>
{authenticatedUser.loginState === 'loggingIn' ? (
new Array(5).fill(0).map((_, index) => (
<ListItem
style={styles.listItem}
key={`skeleton-${index}`}
>
<Line expand>
<Column expand>
<Skeleton
variant="rect"
height={skeletonLineHeight}
style={styles.projectSkeleton}
/>
</Column>
</Line>
</ListItem>
))
) : projectFiles && projectFiles.length > 0 ? (
projectFiles.map(file => {
const storageProvider = getStorageProviderByInternalName(
storageProviders,
file.storageProviderName
);
return (
{authenticatedUser.loginState === 'loggingIn'
? new Array(5).fill(0).map((_, index) => (
<ListItem
button
key={file.fileMetadata.fileIdentifier}
onClick={() => {
onOpenRecentFile(file);
}}
style={styles.listItem}
onContextMenu={event =>
openContextMenu(event, file)
}
key={`skeleton-${index}`}
>
<>
{storageProvider &&
storageProvider.renderIcon &&
!isWindowWidthMediumOrLarger && (
<ListItemAvatar
classes={iconClasses}
style={{
marginTop: 8,
alignSelf: 'flex-start',
}}
<Line expand>
<Column expand>
<Skeleton
variant="rect"
height={skeletonLineHeight}
style={styles.projectSkeleton}
/>
</Column>
</Line>
</ListItem>
))
: projectFiles &&
projectFiles.length > 0 &&
projectFiles.map(file => {
const storageProvider = getStorageProviderByInternalName(
storageProviders,
file.storageProviderName
);
return (
<ListItem
button
key={file.fileMetadata.fileIdentifier}
onClick={() => {
onOpenRecentFile(file);
}}
style={styles.listItem}
onContextMenu={event =>
openContextMenu(event, file)
}
>
<>
{storageProvider &&
storageProvider.renderIcon &&
!isWindowWidthMediumOrLarger && (
<ListItemAvatar
classes={iconClasses}
style={{
marginTop: 8,
alignSelf: 'flex-start',
}}
>
{storageProvider.renderIcon({
size: 'small',
})}
</ListItemAvatar>
)}
{isWindowWidthMediumOrLarger ? (
<LineStackLayout
justifyContent="flex-start"
expand
>
{storageProvider.renderIcon({
size: 'small',
})}
</ListItemAvatar>
)}
{isWindowWidthMediumOrLarger ? (
<LineStackLayout
justifyContent="flex-start"
expand
>
<Column expand>
<Line noMargin alignItems="center">
{storageProvider &&
storageProvider.renderIcon && (
<Column expand>
<Line noMargin alignItems="center">
{storageProvider &&
storageProvider.renderIcon && (
<>
{storageProvider.renderIcon({
size: 'small',
})}
<Spacer />
</>
)}
<Text noMargin>
{file.fileMetadata.name || (
<PrettyBreakablePath
path={
file.fileMetadata
.fileIdentifier
}
/>
)}
</Text>
{pendingProject ===
file.fileMetadata
.fileIdentifier && (
<>
{storageProvider.renderIcon({
size: 'small',
})}
<Spacer />
<CircularProgress size={16} />
</>
)}
</Line>
</Column>
<Column expand>
<Text noMargin>
{file.fileMetadata.name || (
<PrettyBreakablePath
path={
file.fileMetadata.fileIdentifier
}
/>
)}
{storageProvider
? i18n._(storageProvider.name)
: ''}
</Text>
</Column>
<Column expand>
{file.fileMetadata.lastModifiedDate && (
<Text noMargin>
{getRelativeOrAbsoluteDisplayDate(
i18n,
file.fileMetadata.lastModifiedDate
)}
</Text>
)}
</Column>
</LineStackLayout>
) : (
<Column expand>
<Line
noMargin
alignItems="center"
justifyContent="space-between"
>
<ListItemText
primary={
file.fileMetadata.name || (
<PrettyBreakablePath
path={
file.fileMetadata
.fileIdentifier
}
/>
)
}
secondary={
file.fileMetadata.lastModifiedDate
? getRelativeOrAbsoluteDisplayDate(
i18n,
file.fileMetadata
.lastModifiedDate
)
: undefined
}
onContextMenu={event =>
openContextMenu(event, file)
}
/>
{pendingProject ===
file.fileMetadata.fileIdentifier && (
<>
<Spacer />
<CircularProgress size={16} />
</>
<CircularProgress size={24} />
)}
</Line>
</Column>
<Column expand>
<Text noMargin>
{storageProvider
? i18n._(storageProvider.name)
: ''}
</Text>
</Column>
<Column expand>
{file.fileMetadata.lastModifiedDate && (
<Text noMargin>
{getRelativeOrAbsoluteDisplayDate(
i18n,
file.fileMetadata.lastModifiedDate
)}
</Text>
)}
</Column>
</LineStackLayout>
) : (
<Column expand>
<Line
noMargin
alignItems="center"
justifyContent="space-between"
>
<ListItemText
primary={
file.fileMetadata.name || (
<PrettyBreakablePath
path={
file.fileMetadata.fileIdentifier
}
/>
)
}
secondary={
file.fileMetadata.lastModifiedDate
? getRelativeOrAbsoluteDisplayDate(
i18n,
file.fileMetadata
.lastModifiedDate
)
: undefined
}
onContextMenu={event =>
openContextMenu(event, file)
}
/>
{pendingProject ===
file.fileMetadata.fileIdentifier && (
<CircularProgress size={24} />
)}
</Line>
</Column>
)}
</>
</ListItem>
);
})
) : (
<ListItem style={styles.listItem}>
<Column expand>
<Paper
variant="outlined"
background="dark"
style={styles.noProjectsContainer}
>
<BackgroundText>
<Trans>No projects yet.</Trans>
</BackgroundText>
<BackgroundText>
<Trans>
Create your first project using one of our
templates or start from scratch.
</Trans>
</BackgroundText>
</Paper>
</Column>
</ListItem>
)}
)}
</>
</ListItem>
);
})}
</List>
</Column>
</Line>

View File

@@ -1,73 +1,87 @@
// @flow
import * as React from 'react';
import { I18n } from '@lingui/react';
import { t, Trans } from '@lingui/macro';
import { Trans } from '@lingui/macro';
import TranslateIcon from '@material-ui/icons/Translate';
import FlatButton from '../../../UI/FlatButton';
import { Column } from '../../../UI/Grid';
import { Line, Column } from '../../../UI/Grid';
import { LineStackLayout } from '../../../UI/Layout';
import UserChip from '../../../UI/User/UserChip';
import ProjectManager from '../../../UI/CustomSvgIcons/ProjectManager';
import Window from '../../../Utils/Window';
import optionalRequire from '../../../Utils/OptionalRequire';
import RaisedButton from '../../../UI/RaisedButton';
import GDevelopThemeContext from '../../../UI/Theme/ThemeContext';
import { useResponsiveWindowWidth } from '../../../UI/Reponsive/ResponsiveWindowMeasurer';
import TextButton from '../../../UI/TextButton';
import IconButton from '../../../UI/IconButton';
import Paper from '../../../UI/Paper';
const electron = optionalRequire('electron');
type Props = {|
hasProject: boolean,
project: ?gdProject,
onOpenProjectManager: () => void,
onOpenProfile: () => void,
onOpenLanguageDialog: () => void,
|};
export const HomePageHeader = ({
hasProject,
project,
onOpenProjectManager,
onOpenProfile,
onOpenLanguageDialog,
}: Props) => {
const GDevelopTheme = React.useContext(GDevelopThemeContext);
const windowWidth = useResponsiveWindowWidth();
return (
<I18n>
{({ i18n }) => (
<LineStackLayout
justifyContent="space-between"
alignItems="center"
noMargin
expand
<Paper
style={{
borderBottom: `1px solid ${GDevelopTheme.home.separator.color}`,
}}
background="medium"
square
>
<IconButton
size="small"
id="main-toolbar-project-manager-button"
onClick={onOpenProjectManager}
tooltip={t`Project Manager`}
color="default"
disabled={!hasProject}
>
<ProjectManager />
</IconButton>
<Column>
<LineStackLayout noMargin alignItems="center">
{!electron && windowWidth !== 'small' && (
<FlatButton
label={<Trans>Download desktop app</Trans>}
onClick={() =>
Window.openExternalURL('https://gdevelop.io/download')
}
/>
)}
<UserChip onOpenProfile={onOpenProfile} />
<TextButton
label={i18n.language.toUpperCase()}
onClick={onOpenLanguageDialog}
icon={<TranslateIcon fontSize="small" />}
/>
<Line expand>
<LineStackLayout
justifyContent="space-between"
alignItems="center"
noMargin
expand
>
<Column>
{!!project && windowWidth !== 'small' && (
<Line noMargin>
<RaisedButton
id="open-project-manager-button"
label={<Trans>Project Manager</Trans>}
onClick={onOpenProjectManager}
primary
/>
</Line>
)}
</Column>
<Column>
<LineStackLayout noMargin alignItems="center">
{!electron && windowWidth !== 'small' && (
<FlatButton
label={<Trans>Download desktop app</Trans>}
onClick={() =>
Window.openExternalURL('https://gdevelop.io/download')
}
/>
)}
<UserChip onOpenProfile={onOpenProfile} />
<TextButton
label={i18n.language.toUpperCase()}
onClick={onOpenLanguageDialog}
icon={<TranslateIcon fontSize="small" />}
/>
</LineStackLayout>
</Column>
</LineStackLayout>
</Column>
</LineStackLayout>
</Line>
</Paper>
)}
</I18n>
);

View File

@@ -123,34 +123,17 @@ export const HomePage = React.memo<Props>(
[isActive]
);
const getProject = React.useCallback(() => {
const getProject = () => {
return undefined;
}, []);
};
const updateToolbar = React.useCallback(
() => {
if (setToolbar)
setToolbar(
<HomePageHeader
hasProject={!!project}
onOpenLanguageDialog={onOpenLanguageDialog}
onOpenProfile={onOpenProfile}
onOpenProjectManager={onOpenProjectManager}
/>
);
},
[
setToolbar,
onOpenLanguageDialog,
onOpenProfile,
onOpenProjectManager,
project,
]
);
const updateToolbar = () => {
if (setToolbar) setToolbar(null);
};
const forceUpdateEditor = React.useCallback(() => {
const forceUpdateEditor = () => {
// No updates to be done
}, []);
};
React.useImperativeHandle(ref, () => ({
getProject,
@@ -167,6 +150,12 @@ export const HomePage = React.memo<Props>(
{({ i18n }) => (
<>
<Column expand noMargin>
<HomePageHeader
project={project}
onOpenLanguageDialog={onOpenLanguageDialog}
onOpenProfile={onOpenProfile}
onOpenProjectManager={onOpenProjectManager}
/>
<Line expand noMargin useFullHeight>
<HomePageMenu
activeTab={activeTab}

View File

@@ -2,15 +2,73 @@
import * as React from 'react';
import optionalRequire from '../Utils/OptionalRequire';
import { useCommandWithOptions } from '../CommandPalette/CommandHooks';
import {
buildMainMenuDeclarativeTemplate,
type BuildMainMenuProps,
type MainMenuCallbacks,
type MainMenuEvent,
} from './MainMenu';
import { getElectronAccelerator } from '../KeyboardShortcuts';
import { useShortcutMap } from '../KeyboardShortcuts';
import { t } from '@lingui/macro';
import { isMacLike } from '../Utils/Platform';
import { type MainMenuProps } from './MainMenu.flow';
const electron = optionalRequire('electron');
const ipcRenderer = electron ? electron.ipcRenderer : null;
type MainMenuEvent =
| 'main-menu-open'
| 'main-menu-open-recent'
| 'main-menu-save'
| 'main-menu-save-as'
| 'main-menu-close'
| 'main-menu-close-app'
| 'main-menu-export'
| 'main-menu-create-blank'
| 'main-menu-create-template'
| 'main-menu-open-project-manager'
| 'main-menu-open-home-page'
| 'main-menu-open-debugger'
| 'main-menu-open-about'
| 'main-menu-open-preferences'
| 'main-menu-open-language'
| 'main-menu-open-profile'
| 'main-menu-open-games-dashboard'
| 'update-status';
type MenuItemTemplate =
| {|
onClickSendEvent?: MainMenuEvent,
onClickOpenLink?: string,
accelerator?: string,
enabled?: boolean,
label?: string,
role?: string,
eventArgs?: any,
|}
| {|
submenu: Array<MenuItemTemplate>,
label: string,
|}
| {|
submenu: Array<MenuItemTemplate>,
role: string,
|}
| {|
type: 'separator',
|}
| {|
role: string,
|};
type RootMenuTemplate =
| {|
label?: string,
role?: string,
submenu: Array<MenuItemTemplate>,
|}
| {|
role: string,
submenu: Array<MenuItemTemplate>,
|}
| {|
submenu: Array<MenuItemTemplate>,
|};
// Custom hook to register and deregister IPC listener
const useIPCEventListener = (ipcEvent: MainMenuEvent, func) => {
React.useEffect(
@@ -24,68 +82,303 @@ const useIPCEventListener = (ipcEvent: MainMenuEvent, func) => {
);
};
const buildAndSendMenuTemplate = (
project,
i18n,
recentProjectFiles,
shortcutMap
) => {
const fileTemplate = {
label: i18n._(t`File`),
submenu: [
{
label: i18n._(t`Create`),
submenu: [
{
label: i18n._(t`New empty project...`),
accelerator: getElectronAccelerator(
shortcutMap['CREATE_NEW_PROJECT']
),
onClickSendEvent: 'main-menu-create-blank',
},
{
label: i18n._(t`New project from template...`),
onClickSendEvent: 'main-menu-create-template',
},
],
},
{ type: 'separator' },
{
label: i18n._(t`Open...`),
accelerator: getElectronAccelerator(shortcutMap['OPEN_PROJECT']),
onClickSendEvent: 'main-menu-open',
},
{
label: i18n._(t`Open Recent`),
submenu: recentProjectFiles.map(item => ({
label: item.fileMetadata.fileIdentifier,
onClickSendEvent: 'main-menu-open-recent',
eventArgs: item,
})),
},
{ type: 'separator' },
{
label: i18n._(t`Save`),
accelerator: getElectronAccelerator(shortcutMap['SAVE_PROJECT']),
onClickSendEvent: 'main-menu-save',
enabled: !!project,
},
{
label: i18n._(t`Save as...`),
accelerator: getElectronAccelerator(shortcutMap['SAVE_PROJECT_AS']),
onClickSendEvent: 'main-menu-save-as',
enabled: !!project,
},
{ type: 'separator' },
{
label: i18n._(t`Export (web, iOS, Android)...`),
accelerator: getElectronAccelerator(shortcutMap['EXPORT_GAME']),
onClickSendEvent: 'main-menu-export',
enabled: !!project,
},
{ type: 'separator' },
{
label: i18n._(t`Close Project`),
accelerator: getElectronAccelerator(shortcutMap['CLOSE_PROJECT']),
onClickSendEvent: 'main-menu-close',
enabled: !!project,
},
],
};
if (!isMacLike()) {
fileTemplate.submenu.push(
{ type: 'separator' },
{
label: i18n._(t`My Profile`),
onClickSendEvent: 'main-menu-open-profile',
},
{
label: i18n._(t`Games Dashboard`),
onClickSendEvent: 'main-menu-open-games-dashboard',
},
{
label: i18n._(t`Preferences`),
onClickSendEvent: 'main-menu-open-preferences',
},
{
label: i18n._(t`Language`),
onClickSendEvent: 'main-menu-open-language',
},
{ type: 'separator' },
{
label: i18n._(t`Exit GDevelop`),
accelerator: getElectronAccelerator(shortcutMap['QUIT_APP']),
onClickSendEvent: 'main-menu-close-app',
}
);
}
const editTemplate = {
label: i18n._(t`Edit`),
submenu: [
{ label: i18n._(t`Undo`), role: 'undo' },
{ label: i18n._(t`Redo`), role: 'redo' },
{ type: 'separator' },
{ label: i18n._(t`Cut`), role: 'cut' },
{ label: i18n._(t`Copy`), role: 'copy' },
{ label: i18n._(t`Paste`), role: 'paste' },
{ label: i18n._(t`Paste and Match Style`), role: 'pasteandmatchstyle' },
{ label: i18n._(t`Delete`), role: 'delete' },
{ label: i18n._(t`Select All`), role: 'selectall' },
],
};
const viewTemplate = {
label: i18n._(t`View`),
submenu: [
{
label: i18n._(t`Show Project Manager`),
accelerator: getElectronAccelerator(
shortcutMap['OPEN_PROJECT_MANAGER']
),
onClickSendEvent: 'main-menu-open-project-manager',
enabled: !!project,
},
{
label: i18n._(t`Show Home`),
onClickSendEvent: 'main-menu-open-home-page',
},
{
label: i18n._(t`Open Debugger`),
onClickSendEvent: 'main-menu-open-debugger',
enabled: !!project,
},
{ type: 'separator' },
{ label: i18n._(t`Toggle Developer Tools`), role: 'toggledevtools' },
{ type: 'separator' },
{ label: i18n._(t`Toggle Fullscreen`), role: 'togglefullscreen' },
],
};
const windowTemplate = {
label: i18n._(t`Window`),
role: 'window',
submenu: [{ label: i18n._(t`Minimize`), role: 'minimize' }],
};
const helpTemplate = {
label: i18n._(t`Help`),
role: 'help',
submenu: [
{
label: i18n._(t`GDevelop website`),
onClickOpenLink: 'http://gdevelop.io',
},
{ type: 'separator' },
{
label: i18n._(t`Community Forums`),
onClickOpenLink: 'https://forum.gdevelop.io',
},
{
label: i18n._(t`Community Discord Chat`),
onClickOpenLink: 'https://discord.gg/gdevelop',
},
{ type: 'separator' },
{
label: i18n._(t`Contribute to GDevelop`),
onClickOpenLink: 'https://gdevelop.io/page/contribute',
},
{
label: i18n._(t`Create Extensions for GDevelop`),
onClickOpenLink:
'https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md',
},
{ type: 'separator' },
{
label: i18n._(t`Help to Translate GDevelop`),
onClickOpenLink: 'https://crowdin.com/project/gdevelop',
},
{
label: i18n._(t`Report a wrong translation`),
onClickOpenLink: 'https://github.com/4ian/GDevelop/issues/969',
},
],
};
if (!isMacLike()) {
helpTemplate.submenu.push(
{ type: 'separator' },
{
label: i18n._(t`About GDevelop`),
onClickSendEvent: 'main-menu-open-about',
}
);
}
const template: Array<RootMenuTemplate> = [
fileTemplate,
editTemplate,
viewTemplate,
windowTemplate,
helpTemplate,
];
if (isMacLike()) {
template.unshift({
label: i18n._(t`GDevelop 5`),
submenu: [
{
label: i18n._(t`About GDevelop`),
onClickSendEvent: 'main-menu-open-about',
},
{ type: 'separator' },
{
label: i18n._(t`My Profile`),
onClickSendEvent: 'main-menu-open-profile',
},
{
label: i18n._(t`Games Dashboard`),
onClickSendEvent: 'main-menu-open-games-dashboard',
},
{
label: i18n._(t`Preferences`),
onClickSendEvent: 'main-menu-open-preferences',
},
{
label: i18n._(t`Language`),
onClickSendEvent: 'main-menu-open-language',
},
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' },
],
});
editTemplate.submenu.push(
{ type: 'separator' },
{
label: i18n._(t`Speech`),
submenu: [{ role: 'startspeaking' }, { role: 'stopspeaking' }],
}
);
windowTemplate.submenu = [
{ role: 'minimize' },
{ role: 'zoom' },
{ type: 'separator' },
{ role: 'front' },
];
}
if (ipcRenderer) {
ipcRenderer.send('set-main-menu', template);
}
};
/**
* Create and update the editor main menu using Electron APIs.
*/
const ElectronMainMenu = ({
props,
callbacks,
}: {|
props: BuildMainMenuProps,
callbacks: MainMenuCallbacks,
|}) => {
const { i18n, project, recentProjectFiles, shortcutMap } = props;
const ElectronMainMenu = (props: MainMenuProps) => {
const { i18n, project, recentProjectFiles, onOpenRecentFile } = props;
const shortcutMap = useShortcutMap();
const language = i18n.language;
// We could use a for loop, but for safety let's write every hook one by
// one to avoid any change at runtime which would break the rules of hooks.
useIPCEventListener('main-menu-open', callbacks.onChooseProject);
useIPCEventListener('main-menu-open-recent', callbacks.onOpenRecentFile);
useIPCEventListener('main-menu-save', callbacks.onSaveProject);
useIPCEventListener('main-menu-save-as', callbacks.onSaveProjectAs);
useIPCEventListener('main-menu-close', callbacks.onCloseProject);
useIPCEventListener('main-menu-close-app', callbacks.onCloseApp);
useIPCEventListener('main-menu-export', callbacks.onExportProject);
useIPCEventListener('main-menu-create-template', callbacks.onCreateProject);
useIPCEventListener('main-menu-create-blank', callbacks.onCreateBlank);
useIPCEventListener('main-menu-open', props.onChooseProject);
useIPCEventListener('main-menu-open-recent', props.onOpenRecentFile);
useIPCEventListener('main-menu-save', props.onSaveProject);
useIPCEventListener('main-menu-save-as', props.onSaveProjectAs);
useIPCEventListener('main-menu-close', props.onCloseProject);
useIPCEventListener('main-menu-close-app', props.onCloseApp);
useIPCEventListener('main-menu-export', props.onExportProject);
useIPCEventListener('main-menu-create-template', props.onCreateProject);
useIPCEventListener('main-menu-create-blank', props.onCreateBlank);
useIPCEventListener(
'main-menu-open-project-manager',
callbacks.onOpenProjectManager
props.onOpenProjectManager
);
useIPCEventListener('main-menu-open-home-page', callbacks.onOpenHomePage);
useIPCEventListener('main-menu-open-debugger', callbacks.onOpenDebugger);
useIPCEventListener('main-menu-open-about', callbacks.onOpenAbout);
useIPCEventListener(
'main-menu-open-preferences',
callbacks.onOpenPreferences
);
useIPCEventListener('main-menu-open-language', callbacks.onOpenLanguage);
useIPCEventListener('main-menu-open-profile', callbacks.onOpenProfile);
useIPCEventListener('main-menu-open-home-page', props.onOpenHomePage);
useIPCEventListener('main-menu-open-debugger', props.onOpenDebugger);
useIPCEventListener('main-menu-open-about', props.onOpenAbout);
useIPCEventListener('main-menu-open-preferences', props.onOpenPreferences);
useIPCEventListener('main-menu-open-language', props.onOpenLanguage);
useIPCEventListener('main-menu-open-profile', props.onOpenProfile);
useIPCEventListener(
'main-menu-open-games-dashboard',
callbacks.onOpenGamesDashboard
props.onOpenGamesDashboard
);
useIPCEventListener('update-status', callbacks.setElectronUpdateStatus);
useIPCEventListener('update-status', props.setElectronUpdateStatus);
React.useEffect(
() => {
if (ipcRenderer) {
ipcRenderer.send(
'set-main-menu',
buildMainMenuDeclarativeTemplate({
project,
i18n,
recentProjectFiles,
shortcutMap,
isApplicationTopLevelMenu: true,
})
);
}
buildAndSendMenuTemplate(project, i18n, recentProjectFiles, shortcutMap);
},
[i18n, language, project, recentProjectFiles, shortcutMap]
);
const { onOpenRecentFile } = callbacks;
useCommandWithOptions('OPEN_RECENT_PROJECT', true, {
generateOptions: React.useCallback(
() =>

View File

@@ -30,12 +30,6 @@ html {
animation: fade-in 0.5s;
}
/* The part of the window which can be drag'n'dropped on desktop (Electron and installed PWA). */
.title-bar-draggable-part {
-webkit-app-region: drag;
app-region: drag;
}
/* A container adding padding to fit on the screen, useful for dialogs on non square screens ("notch"). */
.safe-area-aware-container {
padding-top: env(safe-area-inset-top);

View File

@@ -0,0 +1,30 @@
// @flow
import { type I18n as I18nType } from '@lingui/core';
import { type ElectronUpdateStatus } from './UpdaterTools';
import { type FileMetadataAndStorageProviderName } from '../ProjectsStorage';
export type MainMenuProps = {|
i18n: I18nType,
project: ?gdProject,
onChooseProject: () => void,
onOpenRecentFile: (
fileMetadataAndStorageProviderName: FileMetadataAndStorageProviderName
) => void,
onSaveProject: () => Promise<void>,
onSaveProjectAs: () => void,
onCloseProject: () => Promise<boolean>,
onCloseApp: () => void,
onExportProject: (open?: boolean) => void,
onCreateProject: (open?: boolean) => void,
onCreateBlank: () => void,
onOpenProjectManager: (open?: boolean) => void,
onOpenHomePage: () => void,
onOpenDebugger: () => void,
onOpenAbout: (open?: boolean) => void,
onOpenPreferences: (open?: boolean) => void,
onOpenLanguage: (open?: boolean) => void,
onOpenProfile: (open?: boolean) => void,
onOpenGamesDashboard: (open?: boolean) => void,
setElectronUpdateStatus: ElectronUpdateStatus => void,
recentProjectFiles: Array<FileMetadataAndStorageProviderName>,
|};

View File

@@ -1,457 +0,0 @@
// @flow
import { t } from '@lingui/macro';
import { type I18n as I18nType } from '@lingui/core';
import { type ElectronUpdateStatus } from './UpdaterTools';
import { type FileMetadataAndStorageProviderName } from '../ProjectsStorage';
import { type ShortcutMap } from '../KeyboardShortcuts/DefaultShortcuts';
import {
type MenuDeclarativeItemTemplate,
type MenuItemTemplate,
} from '../UI/Menu/Menu.flow';
import { getElectronAccelerator } from '../KeyboardShortcuts';
import { isMacLike } from '../Utils/Platform';
import Window from '../Utils/Window';
import optionalRequire from '../Utils/OptionalRequire';
const electron = optionalRequire('electron');
export type BuildMainMenuProps = {|
i18n: I18nType,
project: ?gdProject,
recentProjectFiles: Array<FileMetadataAndStorageProviderName>,
shortcutMap: ShortcutMap,
isApplicationTopLevelMenu: boolean,
|};
export type MainMenuCallbacks = {|
onChooseProject: () => void,
onOpenRecentFile: (
fileMetadataAndStorageProviderName: FileMetadataAndStorageProviderName
) => void,
onSaveProject: () => Promise<void>,
onSaveProjectAs: () => void,
onCloseProject: () => Promise<boolean>,
onCloseApp: () => void,
onExportProject: (open?: boolean) => void,
onCreateProject: (open?: boolean) => void,
onCreateBlank: () => void,
onOpenProjectManager: (open?: boolean) => void,
onOpenHomePage: () => void,
onOpenDebugger: () => void,
onOpenAbout: (open?: boolean) => void,
onOpenPreferences: (open?: boolean) => void,
onOpenLanguage: (open?: boolean) => void,
onOpenProfile: (open?: boolean) => void,
onOpenGamesDashboard: (open?: boolean) => void,
setElectronUpdateStatus: ElectronUpdateStatus => void,
|};
export type MainMenuEvent =
| 'main-menu-open'
| 'main-menu-open-recent'
| 'main-menu-save'
| 'main-menu-save-as'
| 'main-menu-close'
| 'main-menu-close-app'
| 'main-menu-export'
| 'main-menu-create-template'
| 'main-menu-create-blank'
| 'main-menu-open-project-manager'
| 'main-menu-open-home-page'
| 'main-menu-open-debugger'
| 'main-menu-open-about'
| 'main-menu-open-preferences'
| 'main-menu-open-language'
| 'main-menu-open-profile'
| 'main-menu-open-games-dashboard'
| 'update-status';
const getMainMenuEventCallback = (
mainMenuEvent: string,
callbacks: MainMenuCallbacks
): Function => {
const mapping = {
'main-menu-open': callbacks.onChooseProject,
'main-menu-open-recent': callbacks.onOpenRecentFile,
'main-menu-save': callbacks.onSaveProject,
'main-menu-save-as': callbacks.onSaveProjectAs,
'main-menu-close': callbacks.onCloseProject,
'main-menu-close-app': callbacks.onCloseApp,
'main-menu-export': callbacks.onExportProject,
'main-menu-create-template': callbacks.onCreateProject,
'main-menu-create-blank': callbacks.onCreateBlank,
'main-menu-open-project-manager': callbacks.onOpenProjectManager,
'main-menu-open-home-page': callbacks.onOpenHomePage,
'main-menu-open-debugger': callbacks.onOpenDebugger,
'main-menu-open-about': callbacks.onOpenAbout,
'main-menu-open-preferences': callbacks.onOpenPreferences,
'main-menu-open-language': callbacks.onOpenLanguage,
'main-menu-open-profile': callbacks.onOpenProfile,
'main-menu-open-games-dashboard': callbacks.onOpenGamesDashboard,
'update-status': callbacks.setElectronUpdateStatus,
};
return mapping[mainMenuEvent] || (() => {});
};
export type MainMenuProps = {|
...BuildMainMenuProps,
...MainMenuCallbacks,
|};
export const buildMainMenuDeclarativeTemplate = ({
shortcutMap,
i18n,
recentProjectFiles,
project,
isApplicationTopLevelMenu,
}: BuildMainMenuProps): Array<MenuDeclarativeItemTemplate> => {
const fileTemplate: MenuDeclarativeItemTemplate = {
label: i18n._(t`File`),
submenu: [
{
label: i18n._(t`Create`),
submenu: [
{
label: i18n._(t`New empty project...`),
accelerator: getElectronAccelerator(
shortcutMap['CREATE_NEW_PROJECT']
),
onClickSendEvent: 'main-menu-create-blank',
},
{
label: i18n._(t`New project from template...`),
onClickSendEvent: 'main-menu-create-template',
},
],
},
{ type: 'separator' },
{
label: i18n._(t`Open...`),
accelerator: getElectronAccelerator(shortcutMap['OPEN_PROJECT']),
onClickSendEvent: 'main-menu-open',
},
{
label: i18n._(t`Open Recent`),
submenu: recentProjectFiles.map(item => ({
label: item.fileMetadata.fileIdentifier,
onClickSendEvent: 'main-menu-open-recent',
eventArgs: item,
})),
},
{ type: 'separator' },
{
label: i18n._(t`Save`),
accelerator: getElectronAccelerator(shortcutMap['SAVE_PROJECT']),
onClickSendEvent: 'main-menu-save',
enabled: !!project,
},
{
label: i18n._(t`Save as...`),
accelerator: getElectronAccelerator(shortcutMap['SAVE_PROJECT_AS']),
onClickSendEvent: 'main-menu-save-as',
enabled: !!project,
},
{ type: 'separator' },
{
label: i18n._(t`Export (web, iOS, Android)...`),
accelerator: getElectronAccelerator(shortcutMap['EXPORT_GAME']),
onClickSendEvent: 'main-menu-export',
enabled: !!project,
},
{ type: 'separator' },
{
label: i18n._(t`Close Project`),
accelerator: getElectronAccelerator(shortcutMap['CLOSE_PROJECT']),
onClickSendEvent: 'main-menu-close',
enabled: !!project,
},
...(!isMacLike() || !isApplicationTopLevelMenu
? [
{ type: 'separator' },
{
label: i18n._(t`My Profile`),
onClickSendEvent: 'main-menu-open-profile',
},
{
label: i18n._(t`Games Dashboard`),
onClickSendEvent: 'main-menu-open-games-dashboard',
},
{
label: i18n._(t`Preferences`),
onClickSendEvent: 'main-menu-open-preferences',
},
{
label: i18n._(t`Language`),
onClickSendEvent: 'main-menu-open-language',
},
// Leaving the app can only be done on the desktop app.
...(!!electron
? [
{ type: 'separator' },
{
label: i18n._(t`Exit GDevelop`),
accelerator: getElectronAccelerator(
shortcutMap['QUIT_APP']
),
onClickSendEvent: 'main-menu-close-app',
},
]
: []),
]
: []),
],
};
// The window is only useful on the desktop app. It will be skipped on the web-app.
const editTemplate: MenuDeclarativeItemTemplate = {
label: i18n._(t`Edit`),
submenu: [
{ label: i18n._(t`Undo`), role: 'undo' },
{ label: i18n._(t`Redo`), role: 'redo' },
{ type: 'separator' },
{ label: i18n._(t`Cut`), role: 'cut' },
{ label: i18n._(t`Copy`), role: 'copy' },
{ label: i18n._(t`Paste`), role: 'paste' },
{ label: i18n._(t`Paste and Match Style`), role: 'pasteandmatchstyle' },
{ label: i18n._(t`Delete`), role: 'delete' },
{ label: i18n._(t`Select All`), role: 'selectall' },
],
};
const viewTemplate: MenuDeclarativeItemTemplate = {
label: i18n._(t`View`),
submenu: [
{
label: i18n._(t`Show Project Manager`),
accelerator: getElectronAccelerator(
shortcutMap['OPEN_PROJECT_MANAGER']
),
onClickSendEvent: 'main-menu-open-project-manager',
enabled: !!project,
},
{
label: i18n._(t`Show Home`),
onClickSendEvent: 'main-menu-open-home-page',
},
{
label: i18n._(t`Open Debugger`),
onClickSendEvent: 'main-menu-open-debugger',
enabled: !!project,
},
// Some Electron specific menu items, not shown in the web-app.
...(!!electron
? [
{ type: 'separator' },
{
label: i18n._(t`Toggle Developer Tools`),
role: 'toggledevtools',
},
{ type: 'separator' },
{ label: i18n._(t`Toggle Fullscreen`), role: 'togglefullscreen' },
]
: []),
],
};
// The window is only useful on the desktop app. It will be skipped on the web-app.
const windowTemplate: MenuDeclarativeItemTemplate = {
label: i18n._(t`Window`),
role: 'window',
submenu: [{ label: i18n._(t`Minimize`), role: 'minimize' }],
};
// The help menu is mostly a collection of links.
const helpTemplate: MenuDeclarativeItemTemplate = {
label: i18n._(t`Help`),
role: 'help',
submenu: [
{
label: i18n._(t`GDevelop website`),
onClickOpenLink: 'http://gdevelop.io',
},
{ type: 'separator' },
{
label: i18n._(t`GDevelop games on Liluo.io`),
onClickOpenLink: 'https://liluo.io',
},
{
label: i18n._(t`Community Forums`),
onClickOpenLink: 'https://forum.gdevelop.io',
},
{
label: i18n._(t`Community Discord Chat`),
onClickOpenLink: 'https://discord.gg/gdevelop',
},
{ type: 'separator' },
{
label: i18n._(t`YouTube channel (tutorials and more)`),
onClickOpenLink: 'https://www.youtube.com/c/gdevelopapp',
},
{
label: i18n._(t`TikTok`),
onClickOpenLink: 'https://tiktok.com/@gdevelop',
},
{
label: i18n._(t`Twitter`),
onClickOpenLink: 'https://twitter.com/gdevelopapp',
},
{
label: i18n._(t`Instagram`),
onClickOpenLink: 'https://www.instagram.com/gdevelopapp',
},
{
label: i18n._(t`Facebook`),
onClickOpenLink: 'https://facebook.com/gdevelopapp',
},
{ type: 'separator' },
{
label: i18n._(t`Buy GDevelop goodies and swag`),
onClickOpenLink: 'https://goodies.gdevelop.io',
},
{
label: i18n._(t`Contribute to GDevelop`),
onClickOpenLink: 'https://gdevelop.io/page/contribute',
},
{
label: i18n._(t`Create Extensions for GDevelop`),
onClickOpenLink:
'https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md',
},
{ type: 'separator' },
{
label: i18n._(t`Help to Translate GDevelop`),
onClickOpenLink: 'https://crowdin.com/project/gdevelop',
},
{
label: i18n._(t`Report a wrong translation`),
onClickOpenLink: 'https://github.com/4ian/GDevelop/issues/969',
},
...(!isMacLike() && isApplicationTopLevelMenu
? [
{ type: 'separator' },
{
label: i18n._(t`About GDevelop`),
onClickSendEvent: 'main-menu-open-about',
},
]
: []),
],
};
// Structure of the menu. Some electron specific menus are not even shown
// on the web-app, because they would not work and make sense at all.
const template: Array<MenuDeclarativeItemTemplate> = [
fileTemplate,
...(!!electron ? [editTemplate] : []),
viewTemplate,
...(!!electron ? [windowTemplate] : []),
helpTemplate,
];
// macOS has a menu with the name of the app.
if (isMacLike() && isApplicationTopLevelMenu) {
template.unshift({
label: i18n._(t`GDevelop 5`),
submenu: [
{
label: i18n._(t`About GDevelop`),
onClickSendEvent: 'main-menu-open-about',
},
{ type: 'separator' },
{
label: i18n._(t`My Profile`),
onClickSendEvent: 'main-menu-open-profile',
},
{
label: i18n._(t`Games Dashboard`),
onClickSendEvent: 'main-menu-open-games-dashboard',
},
{
label: i18n._(t`Preferences`),
onClickSendEvent: 'main-menu-open-preferences',
},
{
label: i18n._(t`Language`),
onClickSendEvent: 'main-menu-open-language',
},
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' },
],
});
// $FlowFixMe - submenu is guaranteed to exist.
editTemplate.submenu.push(
{ type: 'separator' },
{
label: i18n._(t`Speech`),
submenu: [{ role: 'startspeaking' }, { role: 'stopspeaking' }],
}
);
// $FlowFixMe - submenu is guaranteed to exist.
windowTemplate.submenu = [
{ role: 'minimize' },
{ role: 'zoom' },
{ type: 'separator' },
{ role: 'front' },
];
}
return template;
};
export const adaptFromDeclarativeTemplate = (
menuDeclarativeTemplate: Array<MenuDeclarativeItemTemplate>,
callbacks: MainMenuCallbacks
): Array<MenuItemTemplate> => {
const adaptMenuDeclarativeItemTemplate = (
menuTemplate: Array<MenuDeclarativeItemTemplate>
): Array<MenuItemTemplate> =>
menuTemplate.map((menuItemTemplate: MenuDeclarativeItemTemplate) => {
const {
// $FlowFixMe - property can be undefined.
onClickSendEvent,
// $FlowFixMe - property can be undefined.
onClickOpenLink,
// $FlowFixMe - property can be undefined.
eventArgs,
...menuItemTemplateRest
} = menuItemTemplate;
const hasOnClick = onClickSendEvent || onClickOpenLink;
// $FlowFixMe - we're putting both a click and a submenu, so not strictly following the schema.
return {
...menuItemTemplateRest,
click: hasOnClick
? function() {
if (menuItemTemplate.onClickSendEvent) {
const mainMenuEvent = menuItemTemplate.onClickSendEvent;
const callback = getMainMenuEventCallback(
mainMenuEvent,
callbacks
);
if (eventArgs) callback(eventArgs);
else callback();
}
if (menuItemTemplate.onClickOpenLink) {
Window.openExternalURL(menuItemTemplate.onClickOpenLink);
}
}
: undefined,
submenu: menuItemTemplate.submenu
? adaptMenuDeclarativeItemTemplate(menuItemTemplate.submenu)
: undefined,
};
});
return adaptMenuDeclarativeItemTemplate(menuDeclarativeTemplate);
};

View File

@@ -50,13 +50,13 @@ const ProjectTitlebar = React.memo<Props>(
.join(' - ');
Window.setTitle(title);
Window.setTitleBarColor(gdevelopTheme.titlebar.backgroundColor);
Window.setTitleBarColor(gdevelopTheme.toolbar.backgroundColor);
},
[
projectIdentifier,
suffix,
hasUnsavedChanges,
gdevelopTheme.titlebar.backgroundColor,
gdevelopTheme.toolbar.backgroundColor,
storageProviderName,
]
);

View File

@@ -1,101 +0,0 @@
// @flow
import * as React from 'react';
import MenuIcon from '../UI/CustomSvgIcons/Menu';
import IconButton from '../UI/IconButton';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import ThemeContext from '../UI/Theme/ThemeContext';
import optionalRequire from '../Utils/OptionalRequire';
import { isMacLike } from '../Utils/Platform';
import Window, { useWindowControlsOverlayWatcher } from '../Utils/Window';
import { type MenuItemTemplate } from '../UI/Menu/Menu.flow';
import useForceUpdate from '../Utils/UseForceUpdate';
const electron = optionalRequire('electron');
type Props = {|
onBuildMenuTemplate: () => Array<MenuItemTemplate>,
children: React.Node,
|};
const DRAGGABLE_PART_CLASS_NAME = 'title-bar-draggable-part';
const styles = {
container: { display: 'flex', flexShrink: 0, alignItems: 'flex-end' },
leftSideArea: { alignSelf: 'stretch' },
rightSideArea: { alignSelf: 'stretch', flex: 1 },
menuIcon: { marginLeft: 4, marginRight: 4 },
};
/**
* The titlebar containing a menu, the tabs and giving space for window controls.
*/
export default function TabsTitlebar({ children, onBuildMenuTemplate }: Props) {
const forceUpdate = useForceUpdate();
const gdevelopTheme = React.useContext(ThemeContext);
const backgroundColor = gdevelopTheme.titlebar.backgroundColor;
React.useEffect(
() => {
Window.setTitleBarColor(backgroundColor);
},
[backgroundColor]
);
// An installed PWA can have window controls displayed as overlay. If supported,
// we set up a listener to detect any change and force a refresh that will read
// the latest size of the controls.
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
// macOS displays the "traffic lights" on the left.
const isDesktopMacos = !!electron && isMacLike();
let leftSideOffset = isDesktopMacos ? 76 : 0;
const isDesktopWindowsOrLinux = !!electron && !isMacLike();
// Windows and Linux have their "window controls" on the right
let rightSideOffset = isDesktopWindowsOrLinux ? 150 : 0;
// An installed PWA can have window controls displayed as overlay,
// which we measure here to set the offsets.
// $FlowFixMe - this API is not handled by Flow.
const { windowControlsOverlay } = navigator;
if (windowControlsOverlay) {
if (windowControlsOverlay.visible) {
const { x, width } = windowControlsOverlay.getTitlebarAreaRect();
leftSideOffset = x;
rightSideOffset = window.innerWidth - x - width;
}
}
const rightSideAdditionalOffsetToGiveSpaceToDrag = 30;
return (
<div style={{ ...styles.container, backgroundColor }}>
<div
style={{
...styles.leftSideArea,
width: leftSideOffset,
}}
className={DRAGGABLE_PART_CLASS_NAME}
/>
<ElementWithMenu
element={
<IconButton
size="small"
id="gdevelop-main-menu"
style={styles.menuIcon}
color="default"
>
<MenuIcon />
</IconButton>
}
buildMenuTemplate={onBuildMenuTemplate}
/>
{children}
<div
style={{
...styles.rightSideArea,
minWidth:
rightSideOffset + rightSideAdditionalOffsetToGiveSpaceToDrag,
}}
className={DRAGGABLE_PART_CLASS_NAME}
/>
</div>
);
}

View File

@@ -2,13 +2,11 @@
import { type I18n as I18nType } from '@lingui/core';
import * as React from 'react';
import { t, Trans } from '@lingui/macro';
import { LineStackLayout } from '../../UI/Layout';
import RaisedButton from '../../UI/RaisedButton';
import ToolbarIcon from '../../UI/ToolbarIcon';
import TextButton from '../../UI/TextButton';
import ElementWithMenu from '../../UI/Menu/ElementWithMenu';
import { type PreviewState } from '../PreviewState';
import PreviewIcon from '../../UI/CustomSvgIcons/Preview';
import UpdateIcon from '../../UI/CustomSvgIcons/Update';
import PublishIcon from '../../UI/CustomSvgIcons/Publish';
import FlatButtonWithSplitMenu from '../../UI/FlatButtonWithSplitMenu';
import GDevelopThemeContext from '../../UI/Theme/ThemeContext';
export type PreviewAndPublishButtonsProps = {|
onPreviewWithoutHotReload: () => void,
@@ -25,6 +23,7 @@ export type PreviewAndPublishButtonsProps = {|
hasPreviewsRunning: boolean,
previewState: PreviewState,
exportProject: () => void,
hasProject: boolean,
|};
export default function PreviewAndPublishButtons({
@@ -38,18 +37,26 @@ export default function PreviewAndPublishButtons({
previewState,
setPreviewOverride,
exportProject,
hasProject,
}: PreviewAndPublishButtonsProps) {
const previewBuildMenuTemplate = React.useCallback(
const theme = React.useContext(GDevelopThemeContext);
const debugBuildMenuTemplate = React.useCallback(
(i18n: I18nType) => [
{
label: i18n._(t`Start Network Preview (Preview over WiFi/LAN)`),
click: onNetworkPreview,
enabled: canDoNetworkPreview,
},
{ type: 'separator' },
{
label: i18n._(t`Start Preview and Debugger`),
label: i18n._(t`Start Preview with Debugger and Performance Profiler`),
click: onOpenDebugger,
},
],
[onNetworkPreview, onOpenDebugger, canDoNetworkPreview]
);
const previewBuildMenuTemplate = React.useCallback(
(i18n: I18nType) => [
{
label: i18n._(t`Launch another preview in a new window`),
click: onPreviewWithoutHotReload,
@@ -110,32 +117,103 @@ export default function PreviewAndPublishButtons({
hasPreviewsRunning,
setPreviewOverride,
previewState,
onNetworkPreview,
onOpenDebugger,
canDoNetworkPreview,
]
);
const onClickPreview = event => {
if (event.target) event.target.blur();
onHotReloadPreview();
};
return (
<LineStackLayout noMargin>
<FlatButtonWithSplitMenu
primary
onClick={onHotReloadPreview}
disabled={!isPreviewEnabled}
icon={hasPreviewsRunning ? <UpdateIcon /> : <PreviewIcon />}
label={
hasPreviewsRunning ? <Trans>Update</Trans> : <Trans>Preview</Trans>
<React.Fragment>
<ElementWithMenu
element={
<ToolbarIcon
disabled={!isPreviewEnabled}
src="res/ribbon_default/bug32.png"
tooltip={t`Advanced preview options (debugger, network preview...)`}
/>
}
id={'toolbar-preview-button'}
buildMenuTemplate={debugBuildMenuTemplate}
/>
<ElementWithMenu
element={
<TextButton
onClick={onClickPreview}
disabled={!isPreviewEnabled}
icon={
<img
alt="Preview"
src={
hasPreviewsRunning
? 'res/ribbon_default/hotReload64.png'
: previewState.isPreviewOverriden
? 'res/ribbon_default/previewOverride32.png'
: 'res/ribbon_default/preview64.png'
}
width={32}
height={32}
style={{
filter: !isPreviewEnabled
? 'grayscale(100%)'
: theme.gdevelopIconsCSSFilter,
}}
/>
}
label={
hasPreviewsRunning ? (
<Trans>Update</Trans>
) : (
<Trans>Preview</Trans>
)
}
id={'toolbar-preview-button'}
exceptionalTooltipForToolbar={
hasPreviewsRunning ? (
<Trans>
Apply changes to the running preview, right click for more
</Trans>
) : previewState.isPreviewOverriden ? (
<Trans>Preview is overridden, right click for more</Trans>
) : previewState.previewExternalLayoutName ? (
<Trans>
Launch a preview of the external layout inside the scene,
right click for more
</Trans>
) : (
<Trans>
Launch a preview of the scene, right click for more
</Trans>
)
}
/>
}
openMenuWithSecondaryClick
buildMenuTemplate={previewBuildMenuTemplate}
/>
<RaisedButton
primary
<TextButton
onClick={exportProject}
icon={<PublishIcon />}
disabled={!hasProject}
icon={
<img
alt="Publish"
src={'res/ribbon_default/networkicon32.png'}
width={32}
height={32}
style={{
filter: !hasProject
? 'grayscale(100%)'
: theme.gdevelopIconsCSSFilter,
}}
/>
}
label={<Trans>Publish</Trans>}
id={'toolbar-publish-button'}
exceptionalTooltipForToolbar={
<Trans>Export the game (Web, Android, iOS...)</Trans>
}
/>
</LineStackLayout>
</React.Fragment>
);
}

View File

@@ -2,62 +2,90 @@
import { t } from '@lingui/macro';
import * as React from 'react';
import { Toolbar, ToolbarGroup } from '../../UI/Toolbar';
import ToolbarIcon from '../../UI/ToolbarIcon';
import ToolbarSeparator from '../../UI/ToolbarSeparator';
import ElementWithMenu from '../../UI/Menu/ElementWithMenu';
import Window from '../../Utils/Window';
import PreviewAndPublishButtons, {
type PreviewAndPublishButtonsProps,
} from './PreviewAndPublishButtons';
import ProjectManagerIcon from '../../UI/CustomSvgIcons/ProjectManager';
import IconButton from '../../UI/IconButton';
export type MainFrameToolbarProps = {|
showProjectButtons: boolean,
showProjectIcons: boolean,
hasProject: boolean,
toggleProjectManager: () => void,
requestUpdate: ?() => void,
simulateUpdateDownloaded: ?() => void,
simulateUpdateAvailable: ?() => void,
exportProject: () => void,
...PreviewAndPublishButtonsProps,
|};
export type ToolbarInterface = {|
setEditorToolbar: (React.Node | null) => void,
setEditorToolbar: any => void,
|};
export default React.forwardRef<MainFrameToolbarProps, ToolbarInterface>(
function MainframeToolbar(props: MainFrameToolbarProps, ref) {
const [editorToolbar, setEditorToolbar] = React.useState<?React.Node>(null);
const isDev = Window.isDev();
const [editorToolbar, setEditorToolbar] = React.useState<?any>(null);
React.useImperativeHandle(ref, () => ({
setEditorToolbar,
}));
return (
<Toolbar>
{props.showProjectButtons ? (
<>
<ToolbarGroup firstChild>
<IconButton
size="small"
id="main-toolbar-project-manager-button"
onClick={props.toggleProjectManager}
tooltip={t`Project Manager`}
color="default"
>
<ProjectManagerIcon />
</IconButton>
</ToolbarGroup>
<ToolbarGroup>
<PreviewAndPublishButtons
onPreviewWithoutHotReload={props.onPreviewWithoutHotReload}
onOpenDebugger={props.onOpenDebugger}
onNetworkPreview={props.onNetworkPreview}
onHotReloadPreview={props.onHotReloadPreview}
setPreviewOverride={props.setPreviewOverride}
canDoNetworkPreview={props.canDoNetworkPreview}
isPreviewEnabled={props.isPreviewEnabled}
previewState={props.previewState}
hasPreviewsRunning={props.hasPreviewsRunning}
exportProject={props.exportProject}
/>
</ToolbarGroup>
</>
) : null}
<ToolbarGroup firstChild>
{props.showProjectIcons && (
<ToolbarIcon
id="main-toolbar-project-manager-button"
onClick={props.toggleProjectManager}
src="res/ribbon_default/projectManager32.png"
disabled={!props.hasProject}
tooltip={t`Project manager`}
/>
)}
{isDev && props.showProjectIcons && <ToolbarSeparator />}
{isDev && (
<ElementWithMenu
element={<ToolbarIcon src="res/ribbon_default/bug32.png" />}
buildMenuTemplate={() => [
{
label: 'Simulate update downloaded',
disabled: !props.simulateUpdateDownloaded,
click: () => {
props.simulateUpdateDownloaded &&
props.simulateUpdateDownloaded();
},
},
{
label: 'Simulate update available',
disabled: !props.simulateUpdateAvailable,
click: () => {
props.simulateUpdateAvailable &&
props.simulateUpdateAvailable();
},
},
]}
/>
)}
</ToolbarGroup>
<ToolbarGroup>
<PreviewAndPublishButtons
onPreviewWithoutHotReload={props.onPreviewWithoutHotReload}
onOpenDebugger={props.onOpenDebugger}
onNetworkPreview={props.onNetworkPreview}
onHotReloadPreview={props.onHotReloadPreview}
setPreviewOverride={props.setPreviewOverride}
canDoNetworkPreview={props.canDoNetworkPreview}
isPreviewEnabled={props.isPreviewEnabled}
previewState={props.previewState}
hasPreviewsRunning={props.hasPreviewsRunning}
exportProject={props.exportProject}
hasProject={props.hasProject}
/>
</ToolbarGroup>
{editorToolbar || <ToolbarGroup />}
</Toolbar>
);

View File

@@ -104,15 +104,10 @@ import SaveToStorageProviderDialog from '../ProjectsStorage/SaveToStorageProvide
import { useOpenConfirmDialog } from '../ProjectsStorage/OpenConfirmDialog';
import verifyProjectContent from '../ProjectsStorage/ProjectContentChecker';
import UnsavedChangesContext from './UnsavedChangesContext';
import {
type BuildMainMenuProps,
type MainMenuCallbacks,
buildMainMenuDeclarativeTemplate,
adaptFromDeclarativeTemplate,
} from './MainMenu';
import { type MainMenuProps } from './MainMenu.flow';
import useForceUpdate from '../Utils/UseForceUpdate';
import useStateWithCallback from '../Utils/UseSetStateWithCallback';
import { useKeyboardShortcuts, useShortcutMap } from '../KeyboardShortcuts';
import { useKeyboardShortcuts } from '../KeyboardShortcuts';
import useMainFrameCommands from './MainFrameCommands';
import CommandPalette, {
type CommandPaletteInterface,
@@ -161,7 +156,6 @@ import { useOpenInitialDialog } from '../Utils/UseOpenInitialDialog';
import { type InAppTutorialOrchestratorInterface } from '../InAppTutorial/InAppTutorialOrchestrator';
import useInAppTutorialOrchestrator from '../InAppTutorial/useInAppTutorialOrchestrator';
import { FLING_GAME_IN_APP_TUTORIAL_ID } from '../InAppTutorial/InAppTutorialProvider';
import TabsTitlebar from './TabsTitlebar';
const GD_STARTUP_TIMES = global.GD_STARTUP_TIMES || [];
@@ -253,7 +247,7 @@ export type Props = {|
initialGameId?: string,
initialGamesDashboardTab?: string,
introDialog?: React.Element<*>,
renderMainMenu?: (BuildMainMenuProps, MainMenuCallbacks) => React.Node,
renderMainMenu?: MainMenuProps => React.Node,
renderPreviewLauncher?: (
props: PreviewLauncherProps,
ref: (previewLauncher: ?PreviewLauncherInterface) => void
@@ -831,7 +825,7 @@ const MainFrame = (props: Props) => {
};
const openFromFileMetadata = React.useCallback(
async (fileMetadata: FileMetadata): Promise<?State> => {
(fileMetadata: FileMetadata): Promise<?State> => {
const storageProviderOperations = getStorageProviderOperations();
const {
@@ -847,112 +841,107 @@ const MainFrame = (props: Props) => {
fileMetadata,
storageProviderOperations
);
return;
return Promise.resolve();
}
const checkForAutosave = async (): Promise<FileMetadata> => {
const checkForAutosave = (): Promise<FileMetadata> => {
if (!hasAutoSave || !onGetAutoSave) {
return fileMetadata;
return Promise.resolve(fileMetadata);
}
const canOpenAutosave = await hasAutoSave(fileMetadata, true);
if (!canOpenAutosave) return fileMetadata;
return hasAutoSave(fileMetadata, true).then(canOpenAutosave => {
if (!canOpenAutosave) return fileMetadata;
const answer = await showConfirmation({
title: t`This project has an auto-saved version`,
message: t`GDevelop automatically saved a newer version of this project. This new version might differ from the one that you manually saved. Which version would you like to open?`,
dismissButtonLabel: t`My manual save`,
confirmButtonLabel: t`GDevelop auto-save`,
const answer = Window.showConfirmDialog(
i18n._(
t`An autosave file (backup made automatically by GDevelop) that is newer than the project file exists. Would you like to load it instead?`
)
);
if (!answer) return fileMetadata;
return onGetAutoSave(fileMetadata);
});
if (!answer) return fileMetadata;
return onGetAutoSave(fileMetadata);
};
const checkForAutosaveAfterFailure = async (): Promise<?FileMetadata> => {
const checkForAutosaveAfterFailure = (): Promise<?FileMetadata> => {
if (!hasAutoSave || !onGetAutoSave) {
return null;
return Promise.resolve(null);
}
const canOpenAutosave = await hasAutoSave(fileMetadata, false);
if (!canOpenAutosave) return null;
return hasAutoSave(fileMetadata, false).then(canOpenAutosave => {
if (!canOpenAutosave) return null;
const answer = await showConfirmation({
title: t`This project cannot be opened`,
message: t`The project file appears to be corrupted, but an autosave file exists (backup made automatically by GDevelop). Would you like to try to load it instead?`,
confirmButtonLabel: t`Load autosave`,
const answer = Window.showConfirmDialog(
i18n._(
t`The project file appears to be malformed, but an autosave file exists (backup made automatically by GDevelop). Would you like to try to load it instead?`
)
);
if (!answer) return null;
return onGetAutoSave(fileMetadata);
});
if (!answer) return null;
return onGetAutoSave(fileMetadata);
};
setIsLoadingProject(true);
// Try to find an autosave (and ask user if found)
try {
await delay(150);
const autoSavefileMetadata = await checkForAutosave();
let content;
try {
const result = await onOpen(
autoSavefileMetadata,
setLoaderModalProgress
);
content = result.content;
} catch (error) {
return delay(150)
.then(() => checkForAutosave())
.then(fileMetadata => onOpen(fileMetadata, setLoaderModalProgress))
.catch(err => {
// onOpen failed, tried to find again an autosave
const autoSaveAfterFailurefileMetadata = await checkForAutosaveAfterFailure();
if (autoSaveAfterFailurefileMetadata) {
const result = await onOpen(autoSaveAfterFailurefileMetadata);
content = result.content;
return checkForAutosaveAfterFailure().then(fileMetadata => {
if (fileMetadata) {
return onOpen(fileMetadata);
}
throw err;
});
})
.then(({ content }) => {
if (!verifyProjectContent(i18n, content)) {
// The content is not recognized and the user was warned. Abort the opening.
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
return;
}
}
if (!verifyProjectContent(i18n, content)) {
// The content is not recognized and the user was warned. Abort the opening.
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
return;
}
const serializedProject = gd.Serializer.fromJSObject(content);
try {
const state = loadFromSerializedProject(
const serializedProject = gd.Serializer.fromJSObject(content);
return loadFromSerializedProject(
serializedProject,
// Note that fileMetadata is the original, unchanged one, even if we're loading
// an autosave. If we're for some reason loading an autosave, we still consider
// that we're opening the file that was originally requested by the user.
fileMetadata
).then(
state => {
serializedProject.delete();
return Promise.resolve(state);
},
err => {
serializedProject.delete();
throw err;
}
);
serializedProject.delete();
return state;
} catch (err) {
serializedProject.delete();
throw err;
}
} catch (error) {
const errorMessage = getOpenErrorMessage
? getOpenErrorMessage(error)
: t`Check that the path/URL is correct, that you selected a file that is a game file created with GDevelop and that is was not removed.`;
showErrorBox({
message: [
i18n._(t`Unable to open the project.`),
i18n._(errorMessage),
].join('\n'),
errorId: 'project-open-error',
rawError: error,
})
.catch(error => {
const errorMessage = getOpenErrorMessage
? getOpenErrorMessage(error)
: t`Check that the path/URL is correct, that you selected a file that is a game file created with GDevelop and that is was not removed.`;
showErrorBox({
message: [
i18n._(t`Unable to open the project.`),
i18n._(errorMessage),
].join('\n'),
errorId: 'project-open-error',
rawError: error,
});
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
return Promise.reject(error);
});
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
throw error;
}
},
[
i18n,
getStorageProviderOperations,
loadFromSerializedProject,
showConfirmation,
]
[i18n, getStorageProviderOperations, loadFromSerializedProject]
);
const closeApp = React.useCallback((): void => {
@@ -1313,9 +1302,7 @@ const MainFrame = (props: Props) => {
if (!currentProject) return;
const storageProviderOperations = getStorageProviderOperations();
const hasUnsavedChanges = unsavedChanges.hasUnsavedChanges;
if (
hasUnsavedChanges && // Only create an autosave if there are unsaved changes.
preferences.values.autosaveOnPreview &&
storageProviderOperations.onAutoSaveProject &&
currentFileMetadata
@@ -1342,7 +1329,6 @@ const MainFrame = (props: Props) => {
currentFileMetadata,
getStorageProviderOperations,
preferences.values.autosaveOnPreview,
unsavedChanges.hasUnsavedChanges,
]
);
@@ -2395,7 +2381,6 @@ const MainFrame = (props: Props) => {
return showConfirmation({
title: t`Project name changed`,
message: t`Your project name has changed, this will also save the whole project, continue?`,
confirmButtonLabel: t`Save and continue`,
});
}
return true;
@@ -2595,6 +2580,21 @@ const MainFrame = (props: Props) => {
]
);
const simulateUpdateDownloaded = () =>
setElectronUpdateStatus({
status: 'update-downloaded',
message: 'update-downloaded',
info: {
releaseName: 'Fake update',
},
});
const simulateUpdateAvailable = () =>
setElectronUpdateStatus({
status: 'update-available',
message: 'Update available',
});
useKeyboardShortcuts(
commandPaletteRef.current
? commandPaletteRef.current.launchCommand
@@ -2663,46 +2663,33 @@ const MainFrame = (props: Props) => {
const showLoader = isLoadingProject || previewLoading;
const shortcutMap = useShortcutMap();
const buildMainMenuProps = {
i18n: i18n,
project: state.currentProject,
recentProjectFiles: preferences.getRecentProjectFiles(),
shortcutMap,
isApplicationTopLevelMenu: false,
};
const mainMenuCallbacks = {
onChooseProject: () => openOpenFromStorageProviderDialog(),
onOpenRecentFile: openFromFileMetadataWithStorageProvider,
onSaveProject: saveProject,
onSaveProjectAs: saveProjectAs,
onCloseProject: askToCloseProject,
onCloseApp: closeApp,
onExportProject: () => openExportDialog(true),
onCreateProject: () => openCreateProjectDialog(true, null),
onCreateBlank: () => setNewProjectSetupDialogOpen(true),
onOpenProjectManager: () => openProjectManager(true),
onOpenHomePage: openHomePage,
onOpenDebugger: openDebugger,
onOpenAbout: () => openAboutDialog(true),
onOpenPreferences: () => openPreferencesDialog(true),
onOpenLanguage: () => openLanguageDialog(true),
onOpenProfile: () => openProfileDialogWithTab('profile'),
onOpenGamesDashboard: () => openProfileDialogWithTab('games-dashboard'),
setElectronUpdateStatus: setElectronUpdateStatus,
};
return (
<div
className={
'main-frame' /* The root styling, done in CSS to read some CSS variables. */
}
>
<div className="main-frame">
{!!renderMainMenu &&
renderMainMenu(
{ ...buildMainMenuProps, isApplicationTopLevelMenu: true },
mainMenuCallbacks
)}
renderMainMenu({
i18n: i18n,
project: state.currentProject,
onChooseProject: () => openOpenFromStorageProviderDialog(),
onOpenRecentFile: openFromFileMetadataWithStorageProvider,
onSaveProject: saveProject,
onSaveProjectAs: saveProjectAs,
onCloseProject: askToCloseProject,
onCloseApp: closeApp,
onExportProject: () => openExportDialog(true),
onCreateProject: () => openCreateProjectDialog(true, null),
onCreateBlank: () => setNewProjectSetupDialogOpen(true),
onOpenProjectManager: () => openProjectManager(true),
onOpenHomePage: openHomePage,
onOpenDebugger: openDebugger,
onOpenAbout: () => openAboutDialog(true),
onOpenPreferences: () => openPreferencesDialog(true),
onOpenLanguage: () => openLanguageDialog(true),
onOpenProfile: () => openProfileDialogWithTab('profile'),
onOpenGamesDashboard: () =>
openProfileDialogWithTab('games-dashboard'),
setElectronUpdateStatus: setElectronUpdateStatus,
recentProjectFiles: preferences.getRecentProjectFiles(),
})}
<ProjectTitlebar
projectName={currentProject ? currentProject.getName() : null}
fileMetadata={currentFileMetadata}
@@ -2724,6 +2711,7 @@ const MainFrame = (props: Props) => {
title={
state.currentProject ? state.currentProject.getName() : 'No project'
}
displayRightCloseButton
onClose={toggleProjectManager}
/>
{currentProject && (
@@ -2746,6 +2734,15 @@ const MainFrame = (props: Props) => {
onRenameExternalLayout={renameExternalLayout}
onRenameEventsFunctionsExtension={renameEventsFunctionsExtension}
onRenameExternalEvents={renameExternalEvents}
onSaveProject={saveProject}
onSaveProjectAs={saveProjectAs}
onCloseProject={() => {
askToCloseProject();
}}
onExportProject={() => openExportDialog(true)}
onOpenGamesDashboard={() =>
openProfileDialogWithTab('games-dashboard')
}
onOpenResources={() => {
openResources();
openProjectManager(false);
@@ -2772,40 +2769,15 @@ const MainFrame = (props: Props) => {
</EmptyMessage>
)}
</Drawer>
<TabsTitlebar
onBuildMenuTemplate={() =>
adaptFromDeclarativeTemplate(
buildMainMenuDeclarativeTemplate(buildMainMenuProps),
mainMenuCallbacks
)
}
>
<DraggableEditorTabs
hideLabels={false}
editorTabs={state.editorTabs}
onClickTab={(id: number) => _onChangeEditorTab(id)}
onCloseTab={(editorTab: EditorTab) => _onCloseEditorTab(editorTab)}
onCloseOtherTabs={(editorTab: EditorTab) =>
_onCloseOtherEditorTabs(editorTab)
}
onCloseAll={_onCloseAllEditorTabs}
onTabActivated={(editorTab: EditorTab) =>
_onEditorTabActivated(editorTab)
}
onDropTab={onDropEditorTab}
/>
</TabsTitlebar>
<Toolbar
ref={toolbar}
showProjectButtons={
!['start page', 'debugger', null].includes(
getCurrentTab(state.editorTabs)
? getCurrentTab(state.editorTabs).key
: null
)
}
showProjectIcons
hasProject={!!currentProject}
toggleProjectManager={toggleProjectManager}
exportProject={() => openExportDialog(true)}
requestUpdate={props.requestUpdate}
simulateUpdateDownloaded={simulateUpdateDownloaded}
simulateUpdateAvailable={simulateUpdateAvailable}
onOpenDebugger={launchDebuggerAndPreview}
hasPreviewsRunning={hasPreviewsRunning}
onPreviewWithoutHotReload={launchNewPreview}
@@ -2821,6 +2793,20 @@ const MainFrame = (props: Props) => {
}
previewState={previewState}
/>
<DraggableEditorTabs
hideLabels={false}
editorTabs={state.editorTabs}
onClickTab={(id: number) => _onChangeEditorTab(id)}
onCloseTab={(editorTab: EditorTab) => _onCloseEditorTab(editorTab)}
onCloseOtherTabs={(editorTab: EditorTab) =>
_onCloseOtherEditorTabs(editorTab)
}
onCloseAll={_onCloseAllEditorTabs}
onTabActivated={(editorTab: EditorTab) =>
_onEditorTabActivated(editorTab)
}
onDropTab={onDropEditorTab}
/>
<LeaderboardProvider
gameId={
state.currentProject ? state.currentProject.getProjectUuid() : ''

View File

@@ -558,7 +558,6 @@ export default class AuthenticatedUserProvider extends React.Component<
getGameStatsEmail: form.getGameStatsEmail,
getNewsletterEmail: form.getNewsletterEmail,
appLanguage: preferences.language,
donateLink: form.donateLink,
}
);
await this._fetchUserProfileWithoutThrowingErrors();

View File

@@ -2,7 +2,6 @@
import { Trans, t } from '@lingui/macro';
import React from 'react';
import { I18n } from '@lingui/react';
import FlatButton from '../UI/FlatButton';
import Dialog, { DialogPrimaryButton } from '../UI/Dialog';
import {
@@ -40,11 +39,6 @@ export const getUsernameErrorText = (error: ?AuthError) => {
return undefined;
};
const simpleUrlRegex = /^https:\/\/[^ ]+$/;
const donateLinkFormattingErrorMessage = (
<Trans>Please enter a valid URL, starting with https://</Trans>
);
const EditProfileDialog = ({
profile,
onClose,
@@ -56,7 +50,6 @@ const EditProfileDialog = ({
const [description, setDescription] = React.useState(
profile.description || ''
);
const [donateLink, setDonateLink] = React.useState(profile.donateLink || '');
const [getGameStatsEmail, setGetGameStatsEmail] = React.useState(
!!profile.getGameStatsEmail
);
@@ -78,11 +71,6 @@ const EditProfileDialog = ({
!isValidatingUsername &&
(!usernameAvailability || usernameAvailability.isAvailable);
const donateLinkFormattingError =
!!donateLink && !simpleUrlRegex.test(donateLink)
? donateLinkFormattingErrorMessage
: undefined;
const edit = () => {
if (!canEdit) return;
onEdit({
@@ -90,7 +78,6 @@ const EditProfileDialog = ({
description,
getGameStatsEmail,
getNewsletterEmail,
donateLink,
});
};
@@ -113,81 +100,59 @@ const EditProfileDialog = ({
];
return (
<I18n>
{({ i18n }) => (
<Dialog
title={<Trans>Edit your GDevelop profile</Trans>}
actions={actions}
maxWidth="sm"
cannotBeDismissed={updateProfileInProgress}
onRequestClose={onClose}
onApply={edit}
open
>
<ColumnStackLayout noMargin>
<UsernameField
initialUsername={profile.username}
value={username}
onChange={(e, value) => {
setUsername(value);
}}
errorText={getUsernameErrorText(error)}
onAvailabilityChecked={setUsernameAvailability}
onAvailabilityCheckLoading={setIsValidatingUsername}
isValidatingUsername={isValidatingUsername}
disabled={updateProfileInProgress}
/>
<TextField
value={description}
floatingLabelText={<Trans>Bio</Trans>}
fullWidth
multiline
rows={3}
rowsMax={5}
translatableHintText={t`What are you using GDevelop for?`}
onChange={(e, value) => {
setDescription(value);
}}
disabled={updateProfileInProgress}
floatingLabelFixed
/>
<TextField
value={donateLink}
floatingLabelText={<Trans>Donate link</Trans>}
fullWidth
translatableHintText={t`Do you have a Patreon? Ko-fi? Paypal?`}
onChange={(e, value) => {
setDonateLink(value);
}}
disabled={updateProfileInProgress}
floatingLabelFixed
helperMarkdownText={i18n._(
t`Add a link to your donation page. It will be displayed on your Liluo.io profile and game pages.`
)}
errorText={donateLinkFormattingError}
/>
<Checkbox
label={<Trans>I want to receive the GDevelop Newsletter</Trans>}
checked={getNewsletterEmail}
onCheck={(e, value) => {
setGetNewsletterEmail(value);
}}
disabled={updateProfileInProgress}
/>
<Checkbox
label={
<Trans>I want to receive weekly stats about my games</Trans>
}
checked={getGameStatsEmail}
onCheck={(e, value) => {
setGetGameStatsEmail(value);
}}
disabled={updateProfileInProgress}
/>
</ColumnStackLayout>
</Dialog>
)}
</I18n>
<Dialog
title={<Trans>Edit your GDevelop profile</Trans>}
actions={actions}
maxWidth="sm"
cannotBeDismissed={updateProfileInProgress}
onRequestClose={onClose}
onApply={edit}
open
>
<ColumnStackLayout noMargin>
<UsernameField
initialUsername={profile.username}
value={username}
onChange={(e, value) => {
setUsername(value);
}}
errorText={getUsernameErrorText(error)}
onAvailabilityChecked={setUsernameAvailability}
onAvailabilityCheckLoading={setIsValidatingUsername}
isValidatingUsername={isValidatingUsername}
disabled={updateProfileInProgress}
/>
<TextField
value={description}
floatingLabelText={<Trans>Bio</Trans>}
fullWidth
multiline
rows={3}
rowsMax={5}
translatableHintText={t`What are you using GDevelop for?`}
onChange={(e, value) => {
setDescription(value);
}}
disabled={updateProfileInProgress}
/>
<Checkbox
label={<Trans>I want to receive the GDevelop Newsletter</Trans>}
checked={getNewsletterEmail}
onCheck={(e, value) => {
setGetNewsletterEmail(value);
}}
disabled={updateProfileInProgress}
/>
<Checkbox
label={<Trans>I want to receive weekly stats about my games</Trans>}
checked={getGameStatsEmail}
onCheck={(e, value) => {
setGetGameStatsEmail(value);
}}
disabled={updateProfileInProgress}
/>
</ColumnStackLayout>
</Dialog>
);
};

View File

@@ -5,11 +5,7 @@ import * as React from 'react';
import Avatar from '@material-ui/core/Avatar';
import OpenInNew from '@material-ui/icons/OpenInNew';
import { Line, Spacer } from '../UI/Grid';
import {
ColumnStackLayout,
LineStackLayout,
ResponsiveLineStackLayout,
} from '../UI/Layout';
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../UI/Layout';
import PlaceholderLoader from '../UI/PlaceholderLoader';
import { getGravatarUrl } from '../UI/GravatarUrl';
import Text from '../UI/Text';
@@ -21,14 +17,11 @@ import UserAchievements from './Achievement/UserAchievements';
import { type Badge } from '../Utils/GDevelopServices/Badge';
import Window from '../Utils/Window';
import { GDevelopGamesPlatform } from '../Utils/GDevelopServices/ApiConfigs';
import FlatButton from '../UI/FlatButton';
import Coffee from '../UI/CustomSvgIcons/Coffee';
type DisplayedProfile = {
id: string,
+email?: string,
description: ?string,
donateLink: ?string,
username: ?string,
};
@@ -51,7 +44,6 @@ const ProfileDetails = ({
onEditProfile,
badges,
}: Props) => {
const donateLink = profile ? profile.donateLink : null;
return profile ? (
<I18n>
{({ i18n }) => (
@@ -61,7 +53,7 @@ const ProfileDetails = ({
justifyContent="space-between"
noMargin
>
<Line noMargin alignItems="center">
<Line>
<Avatar src={getGravatarUrl(profile.email || '', { size: 40 })} />
<Spacer />
<Text
@@ -78,29 +70,18 @@ const ProfileDetails = ({
</Text>
</Line>
{profile.id && (
<LineStackLayout justifyContent="space-between">
{!isAuthenticatedUserProfile && // Only show on Public Profile.
!!donateLink && (
<RaisedButton
label={<Trans>Buy me a coffee</Trans>}
primary
onClick={() => Window.openExternalURL(donateLink)}
icon={<Coffee />}
/>
)}
<FlatButton
label={<Trans>Access public profile</Trans>}
onClick={() =>
Window.openExternalURL(
GDevelopGamesPlatform.getUserPublicProfileUrl(
profile.id,
profile.username
)
<RaisedButton
label={i18n._(t`Access public profile`)}
onClick={() =>
Window.openExternalURL(
GDevelopGamesPlatform.getUserPublicProfileUrl(
profile.id,
profile.username
)
}
leftIcon={<OpenInNew />}
/>
</LineStackLayout>
)
}
icon={<OpenInNew />}
/>
)}
</ResponsiveLineStackLayout>
{isAuthenticatedUserProfile && profile.email && (
@@ -110,7 +91,7 @@ const ProfileDetails = ({
readOnly
fullWidth
floatingLabelText={<Trans>Email</Trans>}
floatingLabelFixed
floatingLabelFixed={true}
/>
</Line>
)}
@@ -121,7 +102,7 @@ const ProfileDetails = ({
fullWidth
multiline
floatingLabelText={<Trans>Bio</Trans>}
floatingLabelFixed
floatingLabelFixed={true}
translatableHintText={
isAuthenticatedUserProfile
? t`No bio defined. Edit your profile to tell us what you are using GDevelop for!`
@@ -131,21 +112,9 @@ const ProfileDetails = ({
rowsMax={5}
/>
</Line>
{isAuthenticatedUserProfile && (
<Line noMargin>
<TextField
value={profile.donateLink || ''}
readOnly
fullWidth
floatingLabelText={<Trans>Donate link</Trans>}
floatingLabelFixed
translatableHintText={t`No link defined.`}
/>
</Line>
)}
{isAuthenticatedUserProfile && (
<ResponsiveLineStackLayout justifyContent="flex-end" noMargin>
<FlatButton
<RaisedButton
label={<Trans>Change my email</Trans>}
onClick={onChangeEmail}
/>

View File

@@ -52,20 +52,14 @@ const styles = {
const cancelConfirmationTexts = {
title: t`Cancel your subscription`,
message: t`Are you sure you want to cancel your subscription?`,
confirmButtonLabel: t`Cancel my subscription`,
dismissButtonLabel: t`Go back`,
};
const seamlesslyChangeConfirmationTexts = {
title: t`Update your subscription`,
message: t`Are you sure you want to subscribe to this new plan? Your next payment will be pro-rated.`,
confirmButtonLabel: t`Update my subscription`,
dismissButtonLabel: t`Go back`,
};
const cancelAndChangeConfirmationTexts = {
title: t`Cancel then get a new subscription`,
message: t`To get a new subscription, we need to cancel your existing one before you can pay for the new one. The change will be immediate but your payment will NOT be pro-rated (you will have to pay as for a new subscription).`,
confirmButtonLabel: t`Cancel my subscription`,
dismissButtonLabel: t`Go back`,
};
type Props = {|

View File

@@ -18,7 +18,6 @@ import { textEllipsisStyle } from '../UI/TextEllipsis';
import { ExtensionStoreContext } from '../AssetStore/ExtensionStore/ExtensionStoreContext';
import { type ExtensionShortHeader } from '../Utils/GDevelopServices/Extension';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
import Text from '../UI/Text';
const styles = {
noIndentNestedList: {
@@ -33,6 +32,7 @@ type ProjectStructureItemProps = {|
id?: string,
autoGenerateNestedIndicator?: boolean,
initiallyOpen?: boolean,
leftIcon?: React$Element<any>,
indentNestedItems?: boolean,
renderNestedItems: () => Array<React$Element<any> | null>,
primaryText: React.Node,
@@ -44,6 +44,7 @@ type ProjectStructureItemProps = {|
export const ProjectStructureItem = ({
id,
error,
leftIcon,
onRefresh,
indentNestedItems,
autoGenerateNestedIndicator,
@@ -59,20 +60,17 @@ export const ProjectStructureItem = ({
open={open}
autoGenerateNestedIndicator={autoGenerateNestedIndicator}
initiallyOpen={initiallyOpen}
primaryText={
<Text size="sub-title" noMargin>
{primaryText}
</Text>
}
primaryText={primaryText}
renderNestedItems={renderNestedItems}
onReload={onRefresh}
style={{
backgroundColor: gdevelopTheme.listItem.groupBackgroundColor,
borderBottom: `1px solid ${gdevelopTheme.listItem.separatorColor}`,
}}
nestedListStyle={
indentNestedItems ? undefined : styles.noIndentNestedList
}
leftIcon={error ? <WarningIcon /> : undefined}
leftIcon={error ? <WarningIcon /> : leftIcon}
displayReloadButton={!!error}
reloadButtonTooltip={
<Trans>An error has occured in functions. Click to reload them.</Trans>

View File

@@ -10,7 +10,9 @@ import SearchBar, {
useShouldAutofocusSearchbar,
type SearchBarInterface,
} from '../UI/SearchBar';
import ListIcon from '../UI/ListIcon';
import { AddListItem, SearchListItem } from '../UI/ListCommonItem';
import Window from '../Utils/Window';
import VariablesEditorDialog from '../VariablesList/VariablesEditorDialog';
import ProjectPropertiesDialog from './ProjectPropertiesDialog';
import {
@@ -28,10 +30,15 @@ import {
} from '../Utils/Serializer';
import ExtensionsSearchDialog from '../AssetStore/ExtensionStore/ExtensionsSearchDialog';
import Flag from '@material-ui/icons/Flag';
import Close from '@material-ui/icons/Close';
import SettingsApplications from '@material-ui/icons/SettingsApplications';
import PhotoLibrary from '@material-ui/icons/PhotoLibrary';
import Save from '@material-ui/icons/Save';
import VariableTree from '../UI/CustomSvgIcons/VariableTree';
import ArtTrack from '@material-ui/icons/ArtTrack';
import AddToHomeScreen from '@material-ui/icons/AddToHomeScreen';
import FileCopy from '@material-ui/icons/FileCopy';
import TimelineIcon from '@material-ui/icons/Timeline';
import ScenePropertiesDialog from '../SceneEditor/ScenePropertiesDialog';
import SceneVariablesDialog from '../SceneEditor/SceneVariablesDialog';
import { isExtensionNameTaken } from './EventFunctionExtensionNameVerifier';
@@ -47,10 +54,7 @@ import {
ProjectStructureItem,
EventFunctionExtensionItem,
} from './ProjectManagerItems';
import Tooltip from '@material-ui/core/Tooltip';
import SceneIcon from '../UI/CustomSvgIcons/Scene';
import ExternalLayoutIcon from '../UI/CustomSvgIcons/ExternalLayout';
import ExternalEventsIcon from '../UI/CustomSvgIcons/ExternalEvents';
import { Tooltip } from '@material-ui/core';
const LAYOUT_CLIPBOARD_KIND = 'Layout';
const EXTERNAL_LAYOUT_CLIPBOARD_KIND = 'External layout';
@@ -95,6 +99,11 @@ type Props = {|
onOpenExternalEvents: string => void,
onOpenExternalLayout: string => void,
onOpenEventsFunctionsExtension: string => void,
onSaveProject: () => Promise<void>,
onSaveProjectAs: () => void,
onCloseProject: () => void,
onExportProject: () => void,
onOpenGamesDashboard: () => void,
onOpenResources: () => void,
onOpenPlatformSpecificAssets: () => void,
eventsFunctionsExtensionsError: ?Error,
@@ -574,6 +583,47 @@ export default class ProjectManager extends React.Component<Props, State> {
this.setState({ projectPropertiesDialogOpen: false });
};
_renderMenu() {
// If there is already a main menu (as the native one made with
// Electron), don't show it in the Project Manager.
if (Window.hasMainMenu()) return null;
return (
<React.Fragment>
<ListItem
key="save"
primaryText={<Trans>Save</Trans>}
leftIcon={<Save />}
onClick={() => this.props.onSaveProject()}
/>
<ListItem
key="save-as"
primaryText={<Trans>Save as...</Trans>}
leftIcon={<FileCopy />}
onClick={() => this.props.onSaveProjectAs()}
/>
<ListItem
key="export"
primaryText={<Trans>Export</Trans>}
leftIcon={<AddToHomeScreen />}
onClick={() => this.props.onExportProject()}
/>
<ListItem
key="close"
primaryText={<Trans>Close</Trans>}
leftIcon={<Close />}
onClick={() => this.props.onCloseProject()}
/>
<ListItem
key="games-dashboard"
primaryText={<Trans>Published Games Dashboard</Trans>}
leftIcon={<TimelineIcon />}
onClick={() => this.props.onOpenGamesDashboard()}
/>
</React.Fragment>
);
}
_onSearchChange = (text: string) =>
this.setState({
searchText: text,
@@ -638,11 +688,20 @@ export default class ProjectManager extends React.Component<Props, State> {
onOpenSearchExtensionDialog={this._openSearchExtensionDialog}
/>
<List style={styles.list}>
{this._renderMenu()}
<ProjectStructureItem
id={getTabId('game-settings')}
primaryText={<Trans>Game settings</Trans>}
initiallyOpen
leftIcon={
<ListIcon
iconSize={24}
isGDevelopIcon
src="res/ribbon_default/projectManager32.png"
/>
}
initiallyOpen={false}
autoGenerateNestedIndicator={true}
indentNestedItems
renderNestedItems={() => [
<ListItem
id={getTabId('game-properties')}
@@ -677,7 +736,14 @@ export default class ProjectManager extends React.Component<Props, State> {
<ProjectStructureItem
id={getTabId('scenes')}
primaryText={<Trans>Scenes</Trans>}
initiallyOpen
leftIcon={
<ListIcon
iconSize={24}
isGDevelopIcon
src="res/ribbon_default/sceneadd32.png"
/>
}
initiallyOpen={true}
open={forceOpen}
autoGenerateNestedIndicator={!forceOpen}
renderNestedItems={() =>
@@ -693,7 +759,6 @@ export default class ProjectManager extends React.Component<Props, State> {
default:
name === firstLayoutName ? 'true' : undefined,
}}
leftIcon={<SceneIcon />}
primaryText={name}
textEndAdornment={
name === firstLayoutName ? (
@@ -757,7 +822,7 @@ export default class ProjectManager extends React.Component<Props, State> {
onClick={() =>
this._addLayout(project.getLayoutsCount(), i18n)
}
primaryText={<Trans>Add scene</Trans>}
primaryText={<Trans>Click to add a scene</Trans>}
/>
)
}
@@ -765,7 +830,14 @@ export default class ProjectManager extends React.Component<Props, State> {
<ProjectStructureItem
id={getTabId('external-events')}
primaryText={<Trans>External events</Trans>}
initiallyOpen
leftIcon={
<ListIcon
iconSize={24}
isGDevelopIcon
src="res/ribbon_default/externalevents32.png"
/>
}
initiallyOpen={false}
open={forceOpen}
autoGenerateNestedIndicator={!forceOpen}
renderNestedItems={() =>
@@ -778,7 +850,6 @@ export default class ProjectManager extends React.Component<Props, State> {
return (
<Item
key={i}
leftIcon={<ExternalEventsIcon />}
primaryText={name}
editingName={
renamedItemKind === 'external-events' &&
@@ -820,7 +891,9 @@ export default class ProjectManager extends React.Component<Props, State> {
.concat(
<AddListItem
key={'add-external-events'}
primaryText={<Trans>Add external events</Trans>}
primaryText={
<Trans>Click to add external events</Trans>
}
onClick={() =>
this._addExternalEvents(
project.getExternalEventsCount(),
@@ -834,7 +907,14 @@ export default class ProjectManager extends React.Component<Props, State> {
<ProjectStructureItem
id={getTabId('external-layouts')}
primaryText={<Trans>External layouts</Trans>}
initiallyOpen
leftIcon={
<ListIcon
iconSize={24}
isGDevelopIcon
src="res/ribbon_default/externallayout32.png"
/>
}
initiallyOpen={false}
open={forceOpen}
autoGenerateNestedIndicator={!forceOpen}
renderNestedItems={() =>
@@ -847,7 +927,6 @@ export default class ProjectManager extends React.Component<Props, State> {
return (
<Item
key={i}
leftIcon={<ExternalLayoutIcon />}
primaryText={name}
editingName={
renamedItemKind === 'external-layout' &&
@@ -889,7 +968,9 @@ export default class ProjectManager extends React.Component<Props, State> {
.concat(
<AddListItem
key={'add-external-layout'}
primaryText={<Trans>Add external layout</Trans>}
primaryText={
<Trans>Click to add an external layout</Trans>
}
onClick={() =>
this._addExternalLayout(
project.getExternalLayoutsCount(),
@@ -905,7 +986,14 @@ export default class ProjectManager extends React.Component<Props, State> {
primaryText={<Trans>Extensions</Trans>}
error={eventsFunctionsExtensionsError}
onRefresh={onReloadEventsFunctionsExtensions}
initiallyOpen
leftIcon={
<ListIcon
iconSize={24}
isGDevelopIcon
src="res/ribbon_default/function32.png"
/>
}
initiallyOpen={false}
open={forceOpen}
autoGenerateNestedIndicator={
!forceOpen && !eventsFunctionsExtensionsError

View File

@@ -65,7 +65,7 @@ export const onOpen = (
});
};
export const hasAutoSave = async (
export const hasAutoSave = (
fileMetadata: FileMetadata,
compareLastModified: boolean
): Promise<boolean> => {
@@ -73,25 +73,21 @@ export const hasAutoSave = async (
const autoSavePath = filePath + '.autosave';
if (fs.existsSync(autoSavePath)) {
if (!compareLastModified) {
return true;
return Promise.resolve(true);
}
try {
const autoSavedTime = fs.statSync(autoSavePath).mtime.getTime();
const saveTime = fs.statSync(filePath).mtime.getTime();
// When comparing the last modified time, add a 5 seconds margin to avoid
// showing the warning if the user has just saved the project, or if the
// project has been decompressed from a zip file, causing the last modified
// time to be the time of decompression.
if (autoSavedTime > saveTime + 5000) {
return true;
if (autoSavedTime > saveTime) {
return Promise.resolve(true);
}
} catch (err) {
console.error('Unable to compare *.autosave to project', err);
return false;
return Promise.resolve(false);
}
return false;
return Promise.resolve(false);
}
return false;
return Promise.resolve(false);
};
export const onGetAutoSave = (fileMetadata: FileMetadata) => {

View File

@@ -1,12 +1,9 @@
// @flow
import { t } from '@lingui/macro';
import React, { PureComponent } from 'react';
import FolderIcon from '../UI/CustomSvgIcons/Folder';
import TrashIcon from '../UI/CustomSvgIcons/Trash';
import PropertiesPanelIcon from '../UI/CustomSvgIcons/PropertiesPanel';
import { ToolbarGroup } from '../UI/Toolbar';
import ToolbarIcon from '../UI/ToolbarIcon';
import ToolbarSeparator from '../UI/ToolbarSeparator';
import IconButton from '../UI/IconButton';
type Props = {|
onOpenProjectFolder: () => void,
@@ -23,32 +20,24 @@ export class Toolbar extends PureComponent<Props, State> {
return (
<ToolbarGroup lastChild>
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={this.props.onOpenProjectFolder}
src="res/ribbon_default/open32.png"
tooltip={t`Open the project folder`}
>
<FolderIcon />
</IconButton>
/>
<ToolbarSeparator />
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={this.props.onOpenProperties}
src="res/ribbon_default/editprop32.png"
tooltip={t`Open the properties panel`}
>
<PropertiesPanelIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarSeparator />
<ToolbarIcon
onClick={this.props.onDeleteSelection}
src="res/ribbon_default/deleteselected32.png"
disabled={!canDelete}
tooltip={t`Delete the selected resource`}
>
<TrashIcon />
</IconButton>
/>
</ToolbarGroup>
);
}

View File

@@ -18,7 +18,7 @@ import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEd
import ResourcesLoader from '../ResourcesLoader';
import { applyResourceDefaults } from './ResourceUtils';
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
import FlatButtonWithSplitMenu from '../UI/FlatButtonWithSplitMenu';
import RaisedButtonWithSplitMenu from '../UI/RaisedButtonWithSplitMenu';
import { LineStackLayout, ResponsiveLineStackLayout } from '../UI/Layout';
import IconButton from '../UI/IconButton';
import RaisedButton from '../UI/RaisedButton';
@@ -337,7 +337,7 @@ export default class ResourceSelector extends React.Component<Props, State> {
/>
)}
{externalEditors.length > 1 ? (
<FlatButtonWithSplitMenu
<RaisedButtonWithSplitMenu
icon={<Brush fontSize="small" />}
label={i18n._(
this.state.resourceName

View File

@@ -4,22 +4,11 @@ import { type I18n as I18nType } from '@lingui/core';
import * as React from 'react';
import { ToolbarGroup } from '../UI/Toolbar';
import ToolbarSeparator from '../UI/ToolbarSeparator';
import IconButton from '../UI/IconButton';
import ToolbarIcon from '../UI/ToolbarIcon';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import ToolbarCommands from './ToolbarCommands';
import InstancesSelection from '../InstancesEditor/InstancesSelection';
import { type MenuItemTemplate } from '../UI/Menu/Menu.flow';
import ObjectIcon from '../UI/CustomSvgIcons/Object';
import ObjectGroupIcon from '../UI/CustomSvgIcons/ObjectGroup';
import PropertiesPanelIcon from '../UI/CustomSvgIcons/PropertiesPanel';
import InstancesListIcon from '../UI/CustomSvgIcons/InstancesList';
import LayersIcon from '../UI/CustomSvgIcons/Layers';
import UndoIcon from '../UI/CustomSvgIcons/Undo';
import RedoIcon from '../UI/CustomSvgIcons/Redo';
import TrashIcon from '../UI/CustomSvgIcons/Trash';
import GridIcon from '../UI/CustomSvgIcons/Grid';
import ZoomInIcon from '../UI/CustomSvgIcons/ZoomIn';
import EditSceneIcon from '../UI/CustomSvgIcons/EditScene';
type Props = {|
openObjectsList: () => void,
@@ -65,60 +54,63 @@ const Toolbar = (props: Props) => {
}
/>
<ToolbarGroup lastChild>
<IconButton
size="small"
color="default"
<ToolbarIcon
id="toolbar-open-objects-panel-button"
onClick={props.openObjectsList}
src="res/ribbon_default/objects64.png"
tooltip={t`Open Objects Panel`}
>
<ObjectIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
id="toolbar-open-object-groups-panel-button"
onClick={props.openObjectGroupsList}
src={'res/ribbon_default/objectsgroups64.png'}
tooltip={t`Open Object Groups Panel`}
>
<ObjectGroupIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
id="toolbar-open-properties-panel-button"
onClick={props.openProperties}
src="res/ribbon_default/editprop32.png"
tooltip={t`Open Properties Panel`}
>
<PropertiesPanelIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
id="toolbar-open-instances-list-panel-button"
onClick={props.toggleInstancesList}
src="res/ribbon_default/ObjectsPositionsList32.png"
tooltip={t`Open Instances List Panel`}
>
<InstancesListIcon />
</IconButton>
<IconButton
size="small"
color="default"
/>
<ToolbarIcon
id="toolbar-open-layers-panel-button"
onClick={props.toggleLayersList}
src="res/ribbon_default/layers32.png"
tooltip={t`Open Layers Panel`}
>
<LayersIcon />
</IconButton>
/>
<ToolbarSeparator />
<ToolbarIcon
onClick={props.undo}
src="res/ribbon_default/undo32.png"
disabled={!props.canUndo}
tooltip={t`Undo the last changes`}
/>
<ToolbarIcon
onClick={props.redo}
src="res/ribbon_default/redo32.png"
disabled={!props.canRedo}
tooltip={t`Redo the last changes`}
/>
<ToolbarSeparator />
<ToolbarIcon
onClick={props.deleteSelection}
src="res/ribbon_default/deleteselected32.png"
disabled={!props.instancesSelection.getSelectedInstances().length}
tooltip={t`Delete the selected instances from the scene`}
/>
<ToolbarSeparator />
<ElementWithMenu
element={
<IconButton
size="small"
color="default"
<ToolbarIcon
src="res/ribbon_default/grid32.png"
tooltip={t`Toggle/edit grid`}
>
<GridIcon />
</IconButton>
/>
}
buildMenuTemplate={(i18n: I18nType) => [
{
@@ -140,34 +132,12 @@ const Toolbar = (props: Props) => {
},
]}
/>
<ToolbarSeparator />
<IconButton
size="small"
color="default"
onClick={props.undo}
disabled={!props.canUndo}
tooltip={t`Undo the last changes`}
>
<UndoIcon />
</IconButton>
<IconButton
size="small"
color="default"
onClick={props.redo}
disabled={!props.canRedo}
tooltip={t`Redo the last changes`}
>
<RedoIcon />
</IconButton>
<ElementWithMenu
element={
<IconButton
size="small"
color="default"
<ToolbarIcon
src="res/ribbon_default/zoom32.png"
tooltip={t`Change editor zoom`}
>
<ZoomInIcon />
</IconButton>
/>
}
buildMenuTemplate={(i18n: I18nType) => [
...props.getContextMenuZoomItems(i18n),
@@ -182,25 +152,13 @@ const Toolbar = (props: Props) => {
{ label: '400%', click: () => props.setZoomFactor(4.0) },
]}
/>
<IconButton
size="small"
color="default"
onClick={props.deleteSelection}
disabled={!props.instancesSelection.getSelectedInstances().length}
tooltip={t`Delete the selected instances from the scene`}
>
<TrashIcon />
</IconButton>
{props.onOpenSettings && <ToolbarSeparator />}
{props.onOpenSettings && (
<IconButton
size="small"
color="default"
<ToolbarIcon
onClick={props.onOpenSettings}
src="res/ribbon_default/pref32.png"
tooltip={t`Open settings`}
>
<EditSceneIcon />
</IconButton>
/>
)}
</ToolbarGroup>
</>

View File

@@ -1,10 +1,6 @@
// @flow
import { isMacLike, isMobile } from '../Utils/Platform';
/**
* Transform a Electron-like accelerator string (https://www.electronjs.org/docs/latest/api/accelerator)
* so that it's user friendly.
*/
export const adaptAcceleratorString = (accelerator: string): string => {
if (isMobile()) {
return ''; // Do not display accelerators on mobile devices
@@ -17,23 +13,13 @@ export const adaptAcceleratorString = (accelerator: string): string => {
.replace(/Alt\+/, '⌥')
.replace(/Option\+/, '⌥')
.replace(/Delete/, '⌦')
.replace(/Backspace/, '⌫')
.replace(/numadd/, '+')
.replace(/numsub/, '-')
.replace(/num1/, '1')
.replace(/num2/, '2')
.replace(/num3/, '3');
.replace(/Backspace/, '⌫');
} else {
return accelerator
.replace(/CmdOrCtrl\+/, 'Ctrl+')
.replace(/CommandOrControl\+/, 'Ctrl+')
.replace(/Super\+/, 'Win+')
.replace(/Option\+/, 'Alt+')
.replace(/Delete/, 'DEL')
.replace(/numadd/, '+')
.replace(/numsub/, '-')
.replace(/num1/, '1')
.replace(/num2/, '2')
.replace(/num3/, '3');
.replace(/Delete/, 'DEL');
}
};

View File

@@ -1,11 +1,10 @@
// @flow
import * as React from 'react';
import React from 'react';
import { type MessageDescriptor } from '../../Utils/i18n/MessageDescriptor.flow';
// Alert
export type ShowAlertDialogOptions = {|
title: MessageDescriptor,
dismissButtonLabel?: MessageDescriptor,
message: MessageDescriptor,
|};
export type ShowAlertDialogOptionsWithCallback = {|
@@ -16,8 +15,6 @@ export type ShowAlertDialogOptionsWithCallback = {|
// Confirm
export type ShowConfirmDialogOptions = {|
title: MessageDescriptor,
confirmButtonLabel?: MessageDescriptor,
dismissButtonLabel?: MessageDescriptor,
message: MessageDescriptor,
|};
export type ShowConfirmDialogOptionsWithCallback = {|
@@ -28,8 +25,6 @@ export type ShowConfirmDialogOptionsWithCallback = {|
// Confirm Delete
export type ShowConfirmDeleteDialogOptions = {|
title: MessageDescriptor,
confirmButtonLabel?: MessageDescriptor,
dismissButtonLabel?: MessageDescriptor,
message: MessageDescriptor,
fieldMessage: MessageDescriptor,
confirmText: string,

View File

@@ -13,7 +13,6 @@ type Props = {|
title: MessageDescriptor,
message: MessageDescriptor,
onDismiss: () => void,
dismissButtonLabel?: MessageDescriptor,
|};
function AlertDialog(props: Props) {
@@ -28,13 +27,7 @@ function AlertDialog(props: Props) {
<FlatButton
key="dismiss"
keyboardFocused
label={
props.dismissButtonLabel ? (
i18n._(props.dismissButtonLabel)
) : (
<Trans>OK</Trans>
)
}
label={<Trans>OK</Trans>}
onClick={props.onDismiss}
/>,
]}

View File

@@ -76,7 +76,6 @@ function ConfirmProvider({ children }: Props) {
setAlertDialogOpen(false);
alertDialogConfig.callback();
}}
dismissButtonLabel={alertDialogConfig.dismissButtonLabel}
title={alertDialogConfig.title}
message={alertDialogConfig.message}
/>
@@ -88,12 +87,10 @@ function ConfirmProvider({ children }: Props) {
setConfirmDialogOpen(false);
confirmDialogConfig.callback(true);
}}
confirmButtonLabel={confirmDialogConfig.confirmButtonLabel}
onDismiss={() => {
setConfirmDialogOpen(false);
confirmDialogConfig.callback(false);
}}
dismissButtonLabel={confirmDialogConfig.dismissButtonLabel}
title={confirmDialogConfig.title}
message={confirmDialogConfig.message}
/>
@@ -105,12 +102,10 @@ function ConfirmProvider({ children }: Props) {
setConfirmDeleteDialogOpen(false);
confirmDeleteDialogConfig.callback(true);
}}
confirmButtonLabel={confirmDeleteDialogConfig.confirmButtonLabel}
onDismiss={() => {
setConfirmDeleteDialogOpen(false);
confirmDeleteDialogConfig.callback(false);
}}
dismissButtonLabel={confirmDeleteDialogConfig.dismissButtonLabel}
title={confirmDeleteDialogConfig.title}
message={confirmDeleteDialogConfig.message}
fieldMessage={confirmDeleteDialogConfig.fieldMessage}

View File

@@ -18,8 +18,6 @@ type Props = {|
confirmText: string,
onConfirm: () => void,
onDismiss: () => void,
confirmButtonLabel?: MessageDescriptor,
dismissButtonLabel?: MessageDescriptor,
|};
function ConfirmDeleteDialog(props: Props) {
@@ -46,25 +44,13 @@ function ConfirmDeleteDialog(props: Props) {
actions={[
<FlatButton
key="cancel"
label={
props.dismissButtonLabel ? (
i18n._(props.dismissButtonLabel)
) : (
<Trans>Cancel</Trans>
)
}
label={<Trans>Cancel</Trans>}
primary={false}
onClick={props.onDismiss}
/>,
<DialogPrimaryButton
key="confirm"
label={
props.confirmButtonLabel ? (
i18n._(props.confirmButtonLabel)
) : (
<Trans>Confirm</Trans>
)
}
label={<Trans>Confirm</Trans>}
primary
onClick={onConfirm}
disabled={!canConfirm}

View File

@@ -14,8 +14,6 @@ type Props = {|
message: MessageDescriptor,
onConfirm: () => void,
onDismiss: () => void,
confirmButtonLabel?: MessageDescriptor,
dismissButtonLabel?: MessageDescriptor,
// TODO: Add notion of level (info, warning, error)
// level: string,
|};
@@ -31,26 +29,13 @@ function ConfirmDialog(props: Props) {
<FlatButton
key="dismiss"
keyboardFocused
label={
props.dismissButtonLabel ? (
i18n._(props.dismissButtonLabel)
) : (
<Trans>Cancel</Trans>
)
}
label={<Trans>Cancel</Trans>}
onClick={props.onDismiss}
/>,
<DialogPrimaryButton
key="confirm"
label={
props.confirmButtonLabel ? (
i18n._(props.confirmButtonLabel)
) : (
<Trans>Confirm</Trans>
)
}
label={<Trans>Confirm</Trans>}
onClick={props.onConfirm}
primary
/>,
]}
maxWidth="xs"

View File

@@ -78,13 +78,14 @@ type ClosableTabsProps = {|
|};
export const ClosableTabs = ({ hideLabels, children }: ClosableTabsProps) => {
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const tabItemContainerStyle = {
maxWidth: '100%', // Tabs should take all width
flexShrink: 0, // Tabs height should never be reduced
display: hideLabels ? 'none' : 'flex',
flexWrap: 'nowrap', // Single line of tab...
overflowX: 'overlay', // ...scroll horizontally if needed
overflowY: 'hidden', // ...never scroll vertically (useful on Safari)
marginTop: 6,
overflowX: 'auto', // ...scroll horizontally if needed
backgroundColor: gdevelopTheme.closableTabs.containerBackgroundColor,
};
return (
@@ -171,16 +172,7 @@ export function ClosableTab({
flexShrink: 0, // Tabs are never resized to fit in flex container
position: 'relative',
display: 'inline-block',
marginRight: 2,
borderTopRightRadius: 8,
borderTopLeftRadius: 8,
borderTop: '1px solid black',
borderRight: '1px solid black',
borderLeft: '1px solid black',
borderBottom: 'none',
borderColor: active
? gdevelopTheme.closableTabs.selectedBorderColor
: gdevelopTheme.closableTabs.backgroundColor,
marginRight: 1,
backgroundColor: !active
? gdevelopTheme.closableTabs.backgroundColor
: gdevelopTheme.closableTabs.selectedBackgroundColor,

View File

@@ -19,7 +19,10 @@ type Props = {|
onChange?: ColorChangeHandler,
onChangeComplete?: ColorChangeHandler,
disableAlpha?: boolean,
disabled?: boolean,
|};
type State = {|
displayColorPicker: boolean,
|};
const styles = {
@@ -38,10 +41,6 @@ const styles = {
display: 'inline-block',
cursor: 'pointer',
},
disabled: {
opacity: 0.2,
cursor: 'default',
},
popover: {
// Ensure the popover is above everything (modal, dialog, snackbar, tooltips, etc).
// There will be only one ColorPicker opened at a time, so it's fair to put the
@@ -50,73 +49,67 @@ const styles = {
},
};
const ColorPicker = ({
color,
style,
onChange,
onChangeComplete,
disableAlpha,
disabled,
}: Props) => {
const swatchRef = React.useRef<?HTMLDivElement>(null);
const [displayColorPicker, setDisplayColorPicker] = React.useState(false);
const handleClick = () => {
if (disabled) return;
setDisplayColorPicker(!displayColorPicker);
class ColorPicker extends React.Component<Props, State> {
_swatch = React.createRef<HTMLDivElement>();
state = {
displayColorPicker: false,
};
const handleClose = () => {
setDisplayColorPicker(false);
open = () => {
this.setState({ displayColorPicker: true });
};
const displayedColor = color
? color
: {
r: 200,
g: 200,
b: 200,
a: 1,
};
handleClick = () => {
this.setState({ displayColorPicker: !this.state.displayColorPicker });
};
return (
<div style={style}>
<div
style={{
...styles.swatch,
...(disabled ? styles.disabled : {}),
}}
onClick={handleClick}
ref={swatchRef}
>
handleClose = () => {
this.setState({ displayColorPicker: false });
};
render() {
const { style, color, ...otherProps } = this.props;
const displayedColor = color
? color
: {
r: 200,
g: 200,
b: 200,
a: 1,
};
return (
<div style={style}>
<div
style={{
...styles.color,
background: `rgba(${displayedColor.r}, ${displayedColor.g}, ${
displayedColor.b
}, ${displayedColor.a || 1})`,
}}
style={styles.swatch}
onClick={this.handleClick}
ref={this._swatch}
>
{color ? null : '?'}
<div
style={{
...styles.color,
background: `rgba(${displayedColor.r}, ${displayedColor.g}, ${
displayedColor.b
}, ${displayedColor.a || 1})`,
}}
>
{color ? null : '?'}
</div>
</div>
{this.state.displayColorPicker && this._swatch.current ? (
<Popover
open
onClose={this.handleClose}
anchorEl={this._swatch.current}
style={styles.popover}
>
<SketchPicker color={displayedColor} {...otherProps} />
</Popover>
) : null}
</div>
{displayColorPicker && swatchRef.current ? (
<Popover
open
onClose={handleClose}
anchorEl={swatchRef.current}
style={styles.popover}
>
<SketchPicker
color={displayedColor}
onChange={onChange}
onChangeComplete={onChangeComplete}
disableAlpha={disableAlpha}
/>
</Popover>
) : null}
</div>
);
};
);
}
}
export default ColorPicker;

View File

@@ -29,99 +29,97 @@ type Props = {|
onChange: (string, ?number) => void,
color: string,
alpha?: number,
disabled?: boolean,
|};
const ColorField = ({
fullWidth,
disableAlpha,
id,
floatingLabelText,
helperMarkdownText,
onChange,
color,
alpha,
disabled,
}: Props) => {
const [colorValue, setColorValue] = React.useState<string>(color);
// alpha can be equal to 0, so we have to check if it is not undefined
const [alphaValue, setAlphaValue] = React.useState<number>(
!disableAlpha && alpha !== undefined ? alpha : 1
);
const textFieldRef = React.useRef<?TextField>(null);
type State = {|
color: string,
alpha: number,
|};
const handleChange = (newColor: string, newAlpha: number) => {
setColorValue(newColor);
setAlphaValue(newAlpha);
export default class ColorField extends React.Component<Props, State> {
state = {
color: this.props.color,
// alpha can be equal to 0, so we have to check if it is not undefined
alpha:
!this.props.disableAlpha && this.props.alpha !== undefined
? this.props.alpha
: 1,
};
const handleBlur = () => {
_textField: ?TextField = null;
_handleChange = (color: string, alpha: number) => {
this.setState({ color, alpha });
};
_handleBlur = () => {
// change alpha value to be within allowed limits (0-1)
let newAlpha = parseFloat(alpha);
if (newAlpha < 0) newAlpha = 0;
if (newAlpha > 1) newAlpha = 1;
setAlphaValue(newAlpha);
onChange(color, newAlpha);
let alpha = parseFloat(this.state.alpha);
if (alpha < 0) alpha = 0;
if (alpha > 1) alpha = 1;
this.setState({ alpha });
this.props.onChange(this.state.color, alpha);
};
const handlePickerChange = (color: ColorResult) => {
_handlePickerChange = (color: ColorResult) => {
const rgbString = rgbColorToRGBString(color.rgb);
const newAlpha = disableAlpha ? 1 : color.rgb.a;
setColorValue(rgbString);
if (newAlpha) setAlphaValue(newAlpha);
onChange(rgbString, newAlpha);
const alpha = this.props.disableAlpha ? 1 : color.rgb.a;
this.setState({ color: rgbString, alpha });
this.props.onChange(rgbString, alpha);
};
return (
<div
style={{
...styles.container,
width: fullWidth ? '100%' : undefined,
}}
>
<TextField
id={id}
fullWidth={disableAlpha}
style={!disableAlpha ? { width: '70%' } : undefined}
floatingLabelText={floatingLabelText}
floatingLabelFixed={!disabled}
helperMarkdownText={helperMarkdownText}
type="text"
translatableHintText={disabled ? null : t`R;G;B, like 100;200;180`}
value={colorValue}
onChange={event => handleChange(event.target.value, alphaValue)}
onBlur={handleBlur}
ref={textFieldRef}
disabled={disabled}
/>
{!disableAlpha && (
render() {
return (
<div
style={{
...styles.container,
width: this.props.fullWidth ? '100%' : undefined,
}}
>
<TextField
id={`${id || ''}-alpha`}
floatingLabelText="Alpha"
id={this.props.id}
fullWidth={this.props.disableAlpha}
style={!this.props.disableAlpha ? { width: '70%' } : undefined}
floatingLabelText={this.props.floatingLabelText}
floatingLabelFixed
style={{ width: '30%' }}
translatableHintText={t`Number between 0 and 1`}
value={alphaValue.toString()}
onChange={(event, newAlphaValue) =>
handleChange(colorValue, parseFloat(newAlphaValue))
helperMarkdownText={this.props.helperMarkdownText}
type="text"
translatableHintText={t`Text in the format R;G;B, like 100;200;180`}
value={this.state.color}
onChange={event =>
this._handleChange(event.target.value, this.state.alpha)
}
onBlur={handleBlur}
ref={textFieldRef}
type="number"
step={0.1}
disabled={disabled}
/>
)}
<div style={styles.picker}>
<ColorPicker
disableAlpha={disableAlpha}
onChangeComplete={handlePickerChange}
color={rgbStringAndAlphaToRGBColor(colorValue, alphaValue)}
disabled={disabled}
onBlur={this._handleBlur}
ref={textField => (this._textField = textField)}
/>
{!this.props.disableAlpha && (
<TextField
id={`${this.props.id || ''}-alpha`}
floatingLabelText="Alpha"
floatingLabelFixed
style={{ width: '30%' }}
translatableHintText={t`Number between 0 and 1`}
value={this.state.alpha.toString()}
onChange={(event, value) =>
this._handleChange(this.state.color, parseFloat(value))
}
onBlur={this._handleBlur}
ref={textField => (this._textField = textField)}
type="number"
step={0.1}
/>
)}
<div style={styles.picker}>
<ColorPicker
disableAlpha={this.props.disableAlpha}
onChangeComplete={this._handlePickerChange}
color={rgbStringAndAlphaToRGBColor(
this.state.color,
this.state.alpha
)}
/>
</div>
</div>
</div>
);
};
export default ColorField;
);
}
}

View File

@@ -1,31 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 5.5C6.05964 5.5 5.5 6.05964 5.5 6.75V14.25C5.5 14.9404 6.05964 15.5 6.75 15.5H9.375C9.59127 15.5 9.79702 15.5934 9.93943 15.7561L12 18.1111L14.0606 15.7561C14.203 15.5934 14.4087 15.5 14.625 15.5H17.25C17.9404 15.5 18.5 14.9404 18.5 14.25V6.75C18.5 6.05964 17.9404 5.5 17.25 5.5H6.75ZM4 6.75C4 5.23122 5.23122 4 6.75 4H17.25C18.7688 4 20 5.23122 20 6.75V14.25C20 15.7688 18.7688 17 17.25 17H14.9653L12.5644 19.7439C12.422 19.9066 12.2163 20 12 20C11.7837 20 11.578 19.9066 11.4356 19.7439L9.03467 17H6.75C5.23122 17 4 15.7688 4 14.25V6.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 11C8 10.4478 8.44769 10 9 10C9.55231 10 10 10.4478 10 11C10 11.5522 9.55231 12 9 12C8.44769 12 8 11.5522 8 11Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 11C11 10.4478 11.4478 10 12 10C12.5522 10 13 10.4478 13 11C13 11.5522 12.5522 12 12 12C11.4478 12 11 11.5522 11 11Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 11C14 10.4478 14.4478 10 15 10C15.5522 10 16 10.4478 16 11C16 11.5522 15.5522 12 15 12C14.4478 12 14 11.5522 14 11Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,29 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.86269 16.5425V16.5425L2.86268 13.9464C2.86272 12.9799 3.64619 12.1964 4.61266 12.1964H8.99999C9.4142 12.1964 9.74999 12.5322 9.74999 12.9464C9.74999 13.3606 9.4142 13.6964 8.99999 13.6964H4.61271V13.6964C4.47466 13.6964 4.3627 13.8083 4.36268 13.9464V13.9464L4.36269 16.5426C4.36269 16.6806 4.47462 16.7926 4.61266 16.7926L8 16.7926C8.41422 16.7926 8.75 17.1283 8.75 17.5426C8.75 17.9568 8.41422 18.2926 8 18.2926L4.61271 18.2926V18.2926C3.64623 18.2926 2.86268 17.509 2.86269 16.5425Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.6127 9.34224C3.6462 9.34228 2.86265 8.55873 2.86268 7.59222L2.86267 4.99606C2.86271 4.02959 3.64618 3.24608 4.61265 3.24605L16.7304 3.24605C16.7355 3.24576 16.741 3.24545 16.747 3.24515C16.7751 3.24371 16.8131 3.24223 16.8596 3.24151C16.9524 3.24005 17.0808 3.24156 17.2332 3.25267C17.5335 3.27457 17.9502 3.33538 18.3792 3.49966C18.8104 3.66478 19.2795 3.94472 19.64 4.41799C20.0062 4.89887 20.2171 5.52306 20.2171 6.29597C20.2171 7.06887 20.0062 7.69299 19.6398 8.17368C19.2791 8.64675 18.8097 8.92619 18.3784 9.09077C17.9493 9.25452 17.5325 9.31477 17.2322 9.33627C17.0797 9.34718 16.9514 9.34851 16.8586 9.34693C16.8121 9.34614 16.7741 9.34462 16.746 9.34314C16.7402 9.34284 16.7349 9.34254 16.7299 9.34225L4.6127 9.34224ZM16.8163 7.84477C16.7958 7.84309 16.7753 7.84225 16.7548 7.84225L4.61268 7.84224C4.47464 7.84225 4.36268 7.73032 4.36269 7.59228L4.36267 4.99608C4.36268 4.85801 4.47461 4.74607 4.61268 4.74605V4.74605L16.7548 4.74605C16.7751 4.74605 16.7953 4.74523 16.8155 4.74359L16.8128 4.74381L16.8235 4.74319C16.8352 4.7426 16.8555 4.74176 16.8831 4.74132C16.9386 4.74045 17.0222 4.74127 17.1241 4.7487C17.3323 4.76388 17.5937 4.80508 17.8428 4.90046C18.0896 4.99501 18.2986 5.13239 18.4467 5.32685C18.589 5.51369 18.7171 5.80762 18.7171 6.29597C18.7171 6.78434 18.589 7.07788 18.4469 7.26423C18.299 7.4582 18.0903 7.59518 17.8436 7.68934C17.5946 7.78435 17.3333 7.82519 17.1251 7.8401C17.0232 7.84739 16.9396 7.84809 16.8841 7.84715C16.8565 7.84668 16.8362 7.84581 16.8245 7.8452L16.8138 7.84457V7.84457M16.8138 7.84457L16.8163 7.84477Z"
fill="currentColor"
/>
<path
d="M13 16.75C13 16.3358 13.3358 16 13.75 16H15V14.75C15 14.3358 15.3358 14 15.75 14C16.1642 14 16.5 14.3358 16.5 14.75V16H17.75C18.1642 16 18.5 16.3358 18.5 16.75C18.5 17.1642 18.1642 17.5 17.75 17.5H16.5V18.75C16.5 19.1642 16.1642 19.5 15.75 19.5C15.3358 19.5 15 19.1642 15 18.75V17.5H13.75C13.3358 17.5 13 17.1642 13 16.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 16.75C10 13.5744 12.5744 11 15.75 11C18.9257 11 21.5 13.5744 21.5 16.75C21.5 19.9257 18.9257 22.5 15.75 22.5C12.5744 22.5 10 19.9257 10 16.75ZM15.75 12.5C13.4028 12.5 11.5 14.4028 11.5 16.75C11.5 19.0972 13.4028 21 15.75 21C18.0972 21 20 19.0972 20 16.75C20 14.4028 18.0972 12.5 15.75 12.5Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,37 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.86268 16.5425V16.5425L6.86267 13.9464C6.86271 12.9799 7.64618 12.1964 8.61265 12.1964H9.99998C10.4142 12.1964 10.75 12.5321 10.75 12.9464C10.75 13.3606 10.4142 13.6964 9.99998 13.6964H8.6127V13.6964C8.47465 13.6964 8.36269 13.8083 8.36267 13.9464V13.9464L8.36268 16.5426C8.36268 16.6806 8.47461 16.7926 8.61265 16.7926H8.99999C9.41421 16.7926 9.74999 17.1283 9.74999 17.5426C9.74999 17.9568 9.4142 18.2926 8.99999 18.2926H8.6127V18.2926C7.64622 18.2926 6.86267 17.509 6.86268 16.5425Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.6127 9.34224C4.6462 9.34228 3.86265 8.55873 3.86268 7.59222L3.86267 4.99606C3.86271 4.02959 4.64618 3.24608 5.61265 3.24605L17.7304 3.24605C17.7355 3.24576 17.741 3.24545 17.747 3.24515C17.7751 3.24371 17.8131 3.24223 17.8596 3.24151C17.9524 3.24005 18.0808 3.24156 18.2332 3.25267C18.5335 3.27457 18.9502 3.33538 19.3792 3.49966C19.8104 3.66478 20.2795 3.94472 20.64 4.41799C21.0062 4.89887 21.2171 5.52306 21.2171 6.29597C21.2171 7.06887 21.0062 7.69299 20.6398 8.17368C20.2791 8.64675 19.8097 8.92619 19.3784 9.09077C18.9493 9.25452 18.5325 9.31477 18.2322 9.33627C18.0797 9.34718 17.9514 9.34851 17.8586 9.34693C17.8121 9.34614 17.7741 9.34462 17.746 9.34314C17.7402 9.34284 17.7349 9.34254 17.7299 9.34225L5.6127 9.34224ZM17.8163 7.84477C17.7958 7.84309 17.7753 7.84225 17.7548 7.84225L5.61268 7.84224C5.47464 7.84225 5.36268 7.73032 5.36269 7.59228L5.36267 4.99608C5.36268 4.85801 5.47461 4.74607 5.61268 4.74605V4.74605L17.7548 4.74605C17.7751 4.74605 17.7953 4.74523 17.8155 4.74359L17.8128 4.74381L17.8235 4.74319C17.8352 4.7426 17.8555 4.74176 17.8831 4.74132C17.9386 4.74045 18.0222 4.74127 18.1241 4.7487C18.3323 4.76388 18.5937 4.80508 18.8428 4.90046C19.0896 4.99501 19.2986 5.13239 19.4467 5.32685C19.589 5.51369 19.7171 5.80762 19.7171 6.29597C19.7171 6.78434 19.589 7.07788 19.4469 7.26423C19.299 7.4582 19.0903 7.59518 18.8436 7.68934C18.5946 7.78435 18.3333 7.82519 18.1251 7.8401C18.0232 7.84739 17.9396 7.84809 17.8841 7.84715C17.8565 7.84668 17.8362 7.84581 17.8245 7.8452L17.8138 7.84457V7.84457M17.8138 7.84457L17.8163 7.84477Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16.75 12.5C14.4028 12.5 12.5 14.4028 12.5 16.75C12.5 19.0972 14.4028 21 16.75 21C19.0972 21 21 19.0972 21 16.75C21 14.4028 19.0972 12.5 16.75 12.5ZM11 16.75C11 13.5744 13.5744 11 16.75 11C19.9257 11 22.5 13.5744 22.5 16.75C22.5 19.9257 19.9257 22.5 16.75 22.5C13.5744 22.5 11 19.9257 11 16.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 16.75C14 16.3358 14.3358 16 14.75 16H18.75C19.1642 16 19.5 16.3358 19.5 16.75C19.5 17.1642 19.1642 17.5 18.75 17.5H14.75C14.3358 17.5 14 17.1642 14 16.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16.75 14C17.1642 14 17.5 14.3358 17.5 14.75V18.75C17.5 19.1642 17.1642 19.5 16.75 19.5C16.3358 19.5 16 19.1642 16 18.75V14.75C16 14.3358 16.3358 14 16.75 14Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 5.5C8.41015 5.5 5.5 8.41015 5.5 12C5.5 15.5899 8.41015 18.5 12 18.5C15.5899 18.5 18.5 15.5899 18.5 12C18.5 8.41015 15.5899 5.5 12 5.5ZM4 12C4 7.58173 7.58173 4 12 4C16.4183 4 20 7.58173 20 12C20 16.4183 16.4183 20 12 20C7.58173 20 4 16.4183 4 12Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 8.00003C12.4142 8.00003 12.75 8.33582 12.75 8.75003V15.25C12.75 15.6642 12.4142 16 12 16C11.5858 16 11.25 15.6642 11.25 15.25V8.75003C11.25 8.33582 11.5858 8.00003 12 8.00003Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 12C8 11.5858 8.33579 11.25 8.75 11.25H15.25C15.6642 11.25 16 11.5858 16 12C16 12.4142 15.6642 12.75 15.25 12.75H8.75C8.33579 12.75 8 12.4142 8 12Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,37 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="13" height="12" viewBox="0 0 13 12" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.50049 3.99996C8.50049 3.72382 8.72435 3.49996 9.00049 3.49996H9.53382C10.2222 3.49996 10.8883 3.75602 11.3843 4.22098C11.8813 4.68696 12.1672 5.32612 12.1672 5.99996C12.1672 6.6738 11.8813 7.31296 11.3843 7.77894C10.8883 8.2439 10.2222 8.49996 9.53382 8.49996H9.00049C8.72435 8.49996 8.50049 8.2761 8.50049 7.99996C8.50049 7.72382 8.72435 7.49996 9.00049 7.49996H9.53382C9.97705 7.49996 10.3961 7.33459 10.7003 7.0494C11.0035 6.76524 11.1672 6.38699 11.1672 5.99996C11.1672 5.61293 11.0035 5.23468 10.7003 4.95051C10.3961 4.66533 9.97705 4.49996 9.53382 4.49996H9.00049C8.72435 4.49996 8.50049 4.2761 8.50049 3.99996Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.500488 3.99996C0.500488 3.72382 0.724346 3.49996 1.00049 3.49996H9.00049C9.27663 3.49996 9.50049 3.72382 9.50049 3.99996V8.61534C9.50049 9.28796 9.24009 9.9355 8.7727 10.4149C8.30489 10.8947 7.66767 11.1666 7.00049 11.1666H3.00049C2.3333 11.1666 1.69608 10.8947 1.22827 10.4149C0.760885 9.93549 0.500488 9.28796 0.500488 8.61534V3.99996ZM1.50049 4.49996V8.61534C1.50049 9.0308 1.66152 9.42676 1.94427 9.71677C2.22661 10.0063 2.60681 10.1666 3.00049 10.1666H7.00049C7.39417 10.1666 7.77437 10.0063 8.0567 9.71677C8.33946 9.42676 8.50049 9.0308 8.50049 8.61534V4.49996H1.50049Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.00049 0.166626C3.27663 0.166626 3.50049 0.390484 3.50049 0.666626V1.99996C3.50049 2.2761 3.27663 2.49996 3.00049 2.49996C2.72435 2.49996 2.50049 2.2761 2.50049 1.99996V0.666626C2.50049 0.390484 2.72435 0.166626 3.00049 0.166626Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.00049 0.166626C5.27663 0.166626 5.50049 0.390484 5.50049 0.666626V1.99996C5.50049 2.2761 5.27663 2.49996 5.00049 2.49996C4.72435 2.49996 4.50049 2.2761 4.50049 1.99996V0.666626C4.50049 0.390484 4.72435 0.166626 5.00049 0.166626Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.00049 0.166626C7.27663 0.166626 7.50049 0.390484 7.50049 0.666626V1.99996C7.50049 2.2761 7.27663 2.49996 7.00049 2.49996C6.72435 2.49996 6.50049 2.2761 6.50049 1.99996V0.666626C6.50049 0.390484 6.72435 0.166626 7.00049 0.166626Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 5.5C6.05964 5.5 5.5 6.05964 5.5 6.75V17.25C5.5 17.9404 6.05964 18.5 6.75 18.5H17.25C17.9404 18.5 18.5 17.9404 18.5 17.25V6.75C18.5 6.05964 17.9404 5.5 17.25 5.5H6.75ZM4 6.75C4 5.23122 5.23122 4 6.75 4H17.25C18.7688 4 20 5.23122 20 6.75V17.25C20 18.7688 18.7688 20 17.25 20H6.75C5.23122 20 4 18.7688 4 17.25V6.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.19253 10.2483C8.46962 9.94039 8.94384 9.91543 9.25172 10.1925L11.7517 12.4425C11.9098 12.5848 12 12.7874 12 13C12 13.2126 11.9098 13.4152 11.7517 13.5575L9.25172 15.8075C8.94384 16.0846 8.46962 16.0596 8.19253 15.7517C7.91543 15.4438 7.94039 14.9696 8.24828 14.6925L10.1289 13L8.24828 11.3075C7.94039 11.0304 7.91543 10.5562 8.19253 10.2483Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,13 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.48176 10.1022C3.41658 10.0355 3.34154 9.95255 3.27038 9.85494C3.12734 9.65875 3 9.40346 3 9.10285V5.4C3 5.00235 3.32671 4.68 3.72973 4.68C4.13275 4.68 4.45946 5.00235 4.45946 5.4V9.01933C4.4734 9.03774 4.49607 9.06548 4.53157 9.10185L4.53327 9.10359C4.65422 9.22701 4.84706 9.37746 5.10107 9.54312C5.43664 9.76196 5.83151 9.97693 6.20259 10.1606C6.67709 9.13201 7.44229 8.30232 8.37488 7.811C7.90672 7.30018 7.62161 6.62294 7.62161 5.88C7.62161 4.28942 8.92845 3 10.5405 3H13.4594C15.0715 3 16.3783 4.28942 16.3783 5.88C16.3783 6.62294 16.0932 7.30018 15.6251 7.811C16.5577 8.30233 17.3229 9.13202 17.7974 10.1606C18.1415 9.99029 18.5076 9.79231 18.8281 9.5887C19.1017 9.41487 19.3144 9.25425 19.4501 9.12024C19.4967 9.07425 19.5246 9.04043 19.5405 9.01933V5.4C19.5405 5.00235 19.8673 4.68 20.2703 4.68C20.6733 4.68 21 5.00235 21 5.4V9.10285C21 9.57269 20.6889 9.93429 20.4826 10.138C20.2412 10.3764 19.9303 10.6012 19.6182 10.7995C19.1747 11.0812 18.6723 11.3447 18.2327 11.5541C18.2928 11.8922 18.3243 12.2419 18.3243 12.6C18.3243 12.9612 18.2987 13.3139 18.2511 13.6547C18.6881 13.8635 19.1852 14.125 19.624 14.4043C19.9349 14.6021 20.2445 14.8263 20.4849 15.0643C20.6905 15.2677 21 15.6285 21 16.0971V19.8C21 20.1976 20.6733 20.52 20.2703 20.52C19.8673 20.52 19.5405 20.1976 19.5405 19.8V16.1807C19.5247 16.1597 19.4971 16.1263 19.4513 16.0809C19.3166 15.9476 19.1053 15.7877 18.833 15.6144C18.5434 15.4301 18.2161 15.2503 17.9012 15.0913C17.4907 16.2448 16.8266 17.1418 16.1115 17.5818C16.102 17.6175 16.0896 17.6529 16.0742 17.6878C15.694 18.5479 15.1491 19.3581 14.4743 19.963C13.7995 20.5679 12.9549 21 11.9999 21C11.045 21 10.2003 20.5679 9.5255 19.963C8.8507 19.3581 8.30583 18.5479 7.92565 17.6878C7.91021 17.6529 7.89781 17.6175 7.88831 17.5817C7.17331 17.1417 6.5092 16.2447 6.09878 15.0913C5.78267 15.2509 5.45415 15.4315 5.16377 15.6165C4.89233 15.7894 4.6819 15.9489 4.54793 16.0817C4.50257 16.1266 4.47521 16.1598 4.45946 16.1807L4.45946 19.8C4.45946 20.1976 4.13275 20.52 3.72973 20.52C3.32671 20.52 3 20.1976 3 19.8V16.0971C3 15.6293 3.30846 15.2691 3.51359 15.0658C3.75333 14.8281 4.06201 14.6043 4.37208 14.4067C4.81195 14.1265 5.31065 13.8641 5.74887 13.6547C5.70123 13.3139 5.67567 12.9612 5.67567 12.6C5.67567 12.2419 5.70718 11.8921 5.76724 11.5541C5.30116 11.332 4.76148 11.0478 4.29632 10.7444C4.00272 10.5529 3.71083 10.3359 3.48406 10.1045L3.48176 10.1022ZM9.08106 5.88C9.08106 5.08471 9.73448 4.44 10.5405 4.44H13.4594C14.2655 4.44 14.9189 5.08471 14.9189 5.88C14.9189 6.67529 14.2655 7.32 13.4594 7.32H10.5405C9.73448 7.32 9.08106 6.67529 9.08106 5.88ZM10.5069 18.8972C10.1442 18.5721 9.81267 18.1451 9.53714 17.6578C10.5969 16.9696 11.4078 14.7943 12 12.7191C12.5921 14.7943 13.403 16.9695 14.4627 17.6578C14.1872 18.1451 13.8556 18.5721 13.4929 18.8972C13.0002 19.3389 12.4899 19.56 11.9999 19.56C11.5099 19.56 10.9997 19.3389 10.5069 18.8972ZM13.2714 11.8496C13.0197 10.9245 12.8953 9.95802 13.024 9.30751C13.084 9.00424 13.1751 8.89304 13.2105 8.8603L13.2115 8.85943C13.2261 8.84572 13.3172 8.76 13.6569 8.76C15.2476 8.76 16.8648 10.288 16.8648 12.6C16.8648 13.7993 16.5193 14.8534 16.0654 15.5714C15.6784 16.1834 15.3493 16.3673 15.2182 16.42C15.2 16.4046 15.1776 16.3842 15.1508 16.3573C14.9807 16.1868 14.7639 15.8781 14.5229 15.4001C14.0442 14.4505 13.6255 13.1516 13.2714 11.8496ZM10.976 9.30751C11.1046 9.95802 10.9802 10.9245 10.7286 11.8496C10.3745 13.1516 9.95572 14.4505 9.477 15.4001C9.23605 15.8781 9.01927 16.1868 8.84916 16.3573C8.82236 16.3842 8.7999 16.4046 8.78172 16.42C8.65063 16.3673 8.32153 16.1834 7.93457 15.5714C7.48066 14.8534 7.13512 13.7993 7.13512 12.6C7.13512 10.288 8.7524 8.76 10.343 8.76C10.6827 8.76 10.7739 8.84572 10.7885 8.85943L10.7894 8.8603C10.8249 8.89304 10.916 9.00424 10.976 9.30751Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,55 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.75 5.5C5.05964 5.5 4.5 6.05964 4.5 6.75V17.25C4.5 17.9404 5.05964 18.5 5.75 18.5H16.25C16.9404 18.5 17.5 17.9404 17.5 17.25V16C17.5 15.5858 17.8358 15.25 18.25 15.25C18.6642 15.25 19 15.5858 19 16V17.25C19 18.7688 17.7688 20 16.25 20H5.75C4.23122 20 3 18.7688 3 17.25V6.75C3 5.23122 4.23122 4 5.75 4H16.25C16.7321 4 17.1874 4.12471 17.5829 4.3442C17.9451 4.54518 18.0758 5.00171 17.8748 5.3639C17.6738 5.72609 17.2173 5.85678 16.8551 5.6558C16.6765 5.55671 16.471 5.5 16.25 5.5H5.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15 4.25C15.4142 4.25 15.75 4.58579 15.75 5V7C15.75 7.41421 15.4142 7.75 15 7.75C14.5858 7.75 14.25 7.41421 14.25 7V5C14.25 4.58579 14.5858 4.25 15 4.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15 17.25C15.4142 17.25 15.75 17.5858 15.75 18V19C15.75 19.4142 15.4142 19.75 15 19.75C14.5858 19.75 14.25 19.4142 14.25 19V18C14.25 17.5858 14.5858 17.25 15 17.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 4.25C7.16421 4.25 7.5 4.58579 7.5 5V19C7.5 19.4142 7.16421 19.75 6.75 19.75C6.33579 19.75 6 19.4142 6 19V5C6 4.58579 6.33579 4.25 6.75 4.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.25 8.75C3.25 8.33579 3.58579 8 4 8H6.5C6.91421 8 7.25 8.33579 7.25 8.75C7.25 9.16421 6.91421 9.5 6.5 9.5H4C3.58579 9.5 3.25 9.16421 3.25 8.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.25 12C3.25 11.5858 3.58579 11.25 4 11.25H10C10.4142 11.25 10.75 11.5858 10.75 12C10.75 12.4142 10.4142 12.75 10 12.75H4C3.58579 12.75 3.25 12.4142 3.25 12Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.25 15.25C3.25 14.8358 3.58579 14.5 4 14.5H6.5C6.91421 14.5 7.25 14.8358 7.25 15.25C7.25 15.6642 6.91421 16 6.5 16H4C3.58579 16 3.25 15.6642 3.25 15.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18.909 5.68137C19.4841 5.1062 20.4167 5.10621 20.9918 5.68139L22.3186 7.0082C22.8938 7.58335 22.8938 8.51584 22.3187 9.09099L15.6021 15.8076C15.5035 15.9062 15.3793 15.9754 15.2435 16.0073L12.1718 16.7301C11.9189 16.7896 11.6533 16.714 11.4697 16.5303C11.286 16.3467 11.2104 16.0811 11.2699 15.8282L11.9927 12.7565C12.0246 12.6207 12.0938 12.4965 12.1924 12.3979L18.909 5.68137C18.909 5.68137 18.909 5.68136 18.909 5.68137ZM19.9311 6.74203C19.9312 6.74204 19.9311 6.74202 19.9311 6.74203V6.74203ZM19.9504 6.76129L21.2387 8.0496L14.6923 14.596L13.0076 14.9924L13.404 13.3077L19.9504 6.76129Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,32 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<g clipPath="url(#clip0_686_2047)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.99998 1.83337C4.59423 1.83337 1.83331 4.59429 1.83331 8.00004C1.83331 11.4058 4.59423 14.1667 7.99998 14.1667C11.4058 14.1667 14.1666 11.4058 14.1666 8.00004C14.1666 4.59429 11.4058 1.83337 7.99998 1.83337ZM0.833313 8.00004C0.833313 4.042 4.04194 0.833374 7.99998 0.833374C11.9581 0.833374 15.1666 4.04201 15.1666 8.00004C15.1666 11.9581 11.9581 15.1667 7.99998 15.1667C4.04194 15.1667 0.833313 11.9581 0.833313 8.00004Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.59135 6.85555L4.59135 5.89663C4.59137 5.4166 4.98051 5.02743 5.46055 5.02741L9.93634 5.02741L9.94345 5.02717C9.95612 5.02683 9.97264 5.0266 9.99247 5.02676C10.0319 5.02707 10.0856 5.02895 10.1488 5.0349C10.2725 5.04654 10.4475 5.07506 10.6303 5.14738C10.8139 5.22005 11.0234 5.34433 11.187 5.55699C11.3551 5.77555 11.4471 6.052 11.4471 6.37679C11.4471 6.70156 11.355 6.97803 11.1868 7.19655C11.0231 7.40916 10.8135 7.53325 10.6297 7.6057C10.4469 7.6778 10.2718 7.70609 10.1481 7.71757C10.0849 7.72344 10.0313 7.72524 9.99179 7.7255C9.97197 7.72564 9.95545 7.72538 9.94278 7.72503L9.93603 7.72479L5.46059 7.72479C4.98054 7.72479 4.59135 7.3356 4.59135 6.85555ZM9.96992 6.02653C9.9619 6.02692 9.95345 6.02712 9.94543 6.02712L5.59106 6.02712L5.59106 6.72507L9.94543 6.72508C9.95368 6.72508 9.96192 6.72528 9.97016 6.72569M9.97016 6.72569C9.97216 6.72574 9.9776 6.72586 9.98516 6.72581C10.0005 6.72571 10.0251 6.72497 10.0558 6.72213C10.12 6.71618 10.1954 6.70234 10.263 6.67569C10.3297 6.64939 10.3705 6.61807 10.3946 6.58669C10.4142 6.56122 10.4474 6.50511 10.4474 6.37676C10.4474 6.24842 10.4143 6.19213 10.3945 6.16644C10.3702 6.13485 10.3292 6.1034 10.2625 6.07697C10.1948 6.05019 10.1193 6.03626 10.0551 6.03022C10.0245 6.02733 9.9998 6.02656 9.98449 6.02644C9.97693 6.02638 9.97192 6.02648 9.96992 6.02653"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.44141 11.0472C4.96121 11.0472 4.57192 10.6579 4.57193 10.1777L4.57192 9.21853C4.57194 8.73835 4.9612 8.34907 5.44137 8.34906L9.91252 8.34906C9.92533 8.3484 9.94278 8.34772 9.96279 8.3474C10.0026 8.34678 10.0567 8.34744 10.1205 8.35209C10.2451 8.36118 10.4222 8.38656 10.6074 8.4575C10.7941 8.529 11.0061 8.6536 11.1709 8.86989C11.3395 9.09126 11.4297 9.37096 11.4297 9.69881C11.4297 10.0267 11.3395 10.3064 11.1707 10.5277C11.0058 10.744 10.7937 10.8684 10.6069 10.9397C10.4216 11.0104 10.2445 11.0355 10.1198 11.0444C10.056 11.049 10.0019 11.0496 9.96212 11.0489C9.94211 11.0486 9.92544 11.0479 9.91263 11.0472L5.44141 11.0472ZM9.96867 10.0489L9.96748 10.0488L9.96618 10.0487L9.96497 10.0486C9.96685 10.0487 9.9717 10.0489 9.9791 10.0491C9.99408 10.0493 10.0183 10.0491 10.0484 10.047C10.1117 10.0425 10.1851 10.0303 10.2503 10.0054C10.3141 9.98104 10.3524 9.9516 10.3755 9.92141C10.3946 9.8963 10.4297 9.83697 10.4297 9.69881C10.4297 9.56066 10.3946 9.50114 10.3753 9.4758C10.3522 9.44539 10.3136 9.41581 10.2498 9.39137C10.1845 9.36636 10.111 9.35406 10.0478 9.34944C10.0176 9.34725 9.99341 9.34705 9.97843 9.34728C9.97103 9.3474 9.96618 9.34762 9.9643 9.34771M9.96497 10.0486C9.95262 10.0477 9.93994 10.0472 9.92755 10.0472L5.57192 10.0472L5.57192 9.34905L9.92755 9.34906C9.93972 9.34906 9.95217 9.3486 9.9643 9.34771"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_686_2047">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</SvgIcon>
));

View File

@@ -1,38 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<g clipPath="url(#clip0_686_2068)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.99998 1.83337C4.59423 1.83337 1.83331 4.59429 1.83331 8.00004C1.83331 11.4058 4.59423 14.1667 7.99998 14.1667C11.4058 14.1667 14.1666 11.4058 14.1666 8.00004C14.1666 4.59429 11.4058 1.83337 7.99998 1.83337ZM0.833313 8.00004C0.833313 4.042 4.04194 0.833374 7.99998 0.833374C11.9581 0.833374 15.1666 4.04201 15.1666 8.00004C15.1666 11.9581 11.9581 15.1667 7.99998 15.1667C4.04194 15.1667 0.833313 11.9581 0.833313 8.00004Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.92591 5.83337C5.87477 5.83337 5.83331 5.87483 5.83331 5.92597V10.0741C5.83331 10.1253 5.87477 10.1667 5.92591 10.1667H10.0741C10.1252 10.1667 10.1666 10.1253 10.1666 10.0741V5.92597C10.1666 5.87483 10.1252 5.83337 10.0741 5.83337H5.92591ZM4.83331 5.92597C4.83331 5.32254 5.32248 4.83337 5.92591 4.83337H10.0741C10.6775 4.83337 11.1666 5.32254 11.1666 5.92597V10.0741C11.1666 10.6775 10.6775 11.1667 10.0741 11.1667H5.92591C5.32248 11.1667 4.83331 10.6775 4.83331 10.0741V5.92597Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.83331 7.33337C4.83331 7.05723 5.05717 6.83337 5.33331 6.83337H10.6666C10.9428 6.83337 11.1666 7.05723 11.1666 7.33337C11.1666 7.60952 10.9428 7.83337 10.6666 7.83337H5.33331C5.05717 7.83337 4.83331 7.60952 4.83331 7.33337Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.33331 6.83337C7.60946 6.83337 7.83331 7.05723 7.83331 7.33337V10.6667C7.83331 10.9428 7.60946 11.1667 7.33331 11.1667C7.05717 11.1667 6.83331 10.9428 6.83331 10.6667V7.33337C6.83331 7.05723 7.05717 6.83337 7.33331 6.83337Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_686_2068">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 7.75C4 7.33579 4.33579 7 4.75 7H17.25C18.7688 7 20 8.23122 20 9.75V17.25C20 18.7688 18.7688 20 17.25 20H6.75C5.23122 20 4 18.7688 4 17.25V7.75ZM5.5 8.5V17.25C5.5 17.9404 6.05964 18.5 6.75 18.5H17.25C17.9404 18.5 18.5 17.9404 18.5 17.25V9.75C18.5 9.05964 17.9404 8.5 17.25 8.5H5.5Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 6.75C4 5.23122 5.23122 4 6.75 4H10.8127C11.819 4 12.7451 4.54965 13.227 5.43322L14.1584 7.14085C14.3568 7.50449 14.2228 7.96007 13.8591 8.15842C13.4955 8.35677 13.0399 8.22278 12.8416 7.85915L11.9101 6.15145C11.6911 5.74995 11.2702 5.5 10.8127 5.5H6.75C6.05964 5.5 5.5 6.05964 5.5 6.75V11C5.5 11.4142 5.16421 11.75 4.75 11.75C4.33579 11.75 4 11.4142 4 11V6.75Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,31 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 3.25C8.41421 3.25 8.75 3.58579 8.75 4V20C8.75 20.4142 8.41421 20.75 8 20.75C7.58579 20.75 7.25 20.4142 7.25 20V4C7.25 3.58579 7.58579 3.25 8 3.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 3.25C16.4142 3.25 16.75 3.58579 16.75 4V20C16.75 20.4142 16.4142 20.75 16 20.75C15.5858 20.75 15.25 20.4142 15.25 20V4C15.25 3.58579 15.5858 3.25 16 3.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20.75 8C20.75 8.41421 20.4142 8.75 20 8.75L4 8.75C3.58579 8.75 3.25 8.41421 3.25 8C3.25 7.58579 3.58579 7.25 4 7.25L20 7.25C20.4142 7.25 20.75 7.58579 20.75 8Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20.75 16C20.75 16.4142 20.4142 16.75 20 16.75L4 16.75C3.58579 16.75 3.25 16.4142 3.25 16C3.25 15.5858 3.58579 15.25 4 15.25L20 15.25C20.4142 15.25 20.75 15.5858 20.75 16Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,31 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.96967 8.46967C5.26256 8.17678 5.73744 8.17678 6.03033 8.46967L8.53033 10.9697C8.82322 11.2626 8.82322 11.7374 8.53033 12.0303L6.03033 14.5303C5.73744 14.8232 5.26256 14.8232 4.96967 14.5303L2.46967 12.0303C2.17678 11.7374 2.17678 11.2626 2.46967 10.9697L4.96967 8.46967ZM4.06066 11.5L5.5 12.9393L6.93934 11.5L5.5 10.0607L4.06066 11.5Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 7C21.75 7.41421 21.4142 7.75 21 7.75H12C11.5858 7.75 11.25 7.41421 11.25 7C11.25 6.58579 11.5858 6.25 12 6.25H21C21.4142 6.25 21.75 6.58579 21.75 7Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 17C21.75 17.4142 21.4142 17.75 21 17.75H12C11.5858 17.75 11.25 17.4142 11.25 17C11.25 16.5858 11.5858 16.25 12 16.25H21C21.4142 16.25 21.75 16.5858 21.75 17Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 12C21.75 12.4142 21.4142 12.75 21 12.75H12C11.5858 12.75 11.25 12.4142 11.25 12C11.25 11.5858 11.5858 11.25 12 11.25H21C21.4142 11.25 21.75 11.5858 21.75 12Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.6646 3.32918C11.8758 3.22361 12.1243 3.22361 12.3355 3.32918L20.3355 7.32918C20.5895 7.45622 20.75 7.71592 20.75 8C20.75 8.28408 20.5895 8.54378 20.3355 8.67082L12.3355 12.6708C12.1243 12.7764 11.8758 12.7764 11.6646 12.6708L3.66463 8.67082C3.41054 8.54378 3.25004 8.28408 3.25004 8C3.25004 7.71592 3.41054 7.45622 3.66463 7.32918L11.6646 3.32918ZM5.67709 8L12 11.1615L18.323 8L12 4.83853L5.67709 8Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.32922 15.6646C3.51446 15.2941 3.96497 15.1439 4.33545 15.3292L12 19.1615L19.6646 15.3292C20.0351 15.1439 20.4856 15.2941 20.6709 15.6646C20.8561 16.0351 20.7059 16.4856 20.3355 16.6708L12.3355 20.6708C12.1243 20.7764 11.8758 20.7764 11.6646 20.6708L3.66463 16.6708C3.29415 16.4856 3.14398 16.0351 3.32922 15.6646Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.32922 11.6646C3.51446 11.2941 3.96497 11.1439 4.33545 11.3292L12 15.1615L19.6646 11.3292C20.0351 11.1439 20.4856 11.2941 20.6709 11.6646C20.8561 12.0351 20.7059 12.4856 20.3355 12.6708L12.3355 16.6708C12.1243 16.7764 11.8758 16.7764 11.6646 16.6708L3.66463 12.6708C3.29415 12.4856 3.14398 12.0351 3.32922 11.6646Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 7C21.75 7.41421 21.4142 7.75 21 7.75H5C4.58579 7.75 4.25 7.41421 4.25 7C4.25 6.58579 4.58579 6.25 5 6.25H21C21.4142 6.25 21.75 6.58579 21.75 7Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 17C21.75 17.4142 21.4142 17.75 21 17.75H5C4.58579 17.75 4.25 17.4142 4.25 17C4.25 16.5858 4.58579 16.25 5 16.25H21C21.4142 16.25 21.75 16.5858 21.75 17Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.75 12C21.75 12.4142 21.4142 12.75 21 12.75H5C4.58579 12.75 4.25 12.4142 4.25 12C4.25 11.5858 4.58579 11.25 5 11.25H21C21.4142 11.25 21.75 11.5858 21.75 12Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,37 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.6932 4.06562C11.8883 3.97813 12.1116 3.97813 12.3067 4.06562L19.5567 7.31562C19.8264 7.4365 19.9999 7.70448 19.9999 8C19.9999 8.29552 19.8264 8.5635 19.5567 8.68438L12.3067 11.9344C12.1116 12.0219 11.8883 12.0219 11.6932 11.9344L4.44315 8.68438C4.17348 8.5635 3.99994 8.29552 3.99994 8C3.99994 7.70448 4.17348 7.4365 4.44315 7.31562L11.6932 4.06562ZM6.58343 8L11.9999 10.4281L17.4165 8L11.9999 5.57191L6.58343 8Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.06556 15.6932C4.235 15.3152 4.67876 15.1462 5.05673 15.3156L11.9999 18.4281L18.9432 15.3156C19.3211 15.1462 19.7649 15.3152 19.9343 15.6932C20.1038 16.0712 19.9347 16.5149 19.5567 16.6844L12.3067 19.9344C12.1116 20.0219 11.8883 20.0219 11.6932 19.9344L4.44315 16.6844C4.06518 16.5149 3.89612 16.0712 4.06556 15.6932Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.2499 7.25C19.6642 7.25 19.9999 7.58579 19.9999 8V16C19.9999 16.4142 19.6642 16.75 19.2499 16.75C18.8357 16.75 18.4999 16.4142 18.4999 16V8C18.4999 7.58579 18.8357 7.25 19.2499 7.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.74994 7.25C5.16416 7.25 5.49994 7.58579 5.49994 8V16C5.49994 16.4142 5.16416 16.75 4.74994 16.75C4.33573 16.75 3.99994 16.4142 3.99994 16V8C3.99994 7.58579 4.33573 7.25 4.74994 7.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.9999 10.75C12.4142 10.75 12.7499 11.0858 12.7499 11.5V19C12.7499 19.4142 12.4142 19.75 11.9999 19.75C11.5857 19.75 11.2499 19.4142 11.2499 19V11.5C11.2499 11.0858 11.5857 10.75 11.9999 10.75Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,97 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.818 1.86582C12.0132 1.77833 12.2364 1.77833 12.4316 1.86582L17.5566 4.16323C17.8263 4.28412 17.9998 4.55209 17.9998 4.84762C17.9998 5.14314 17.8263 5.41111 17.5566 5.532L12.4316 7.82941C12.2364 7.9169 12.0132 7.9169 11.818 7.82941L6.69303 5.532C6.42336 5.41111 6.24982 5.14314 6.24982 4.84762C6.24982 4.55209 6.42336 4.28412 6.69303 4.16323L11.818 1.86582ZM8.83331 4.84762L12.1248 6.32312L15.4163 4.84762L12.1248 3.37211L8.83331 4.84762Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.31544 10.196C6.48487 9.81802 6.92864 9.64897 7.30661 9.81841L12.1248 11.9783L16.943 9.81841C17.321 9.64897 17.7648 9.81802 17.9342 10.196C18.1036 10.574 17.9346 11.0177 17.5566 11.1872L12.4316 13.4846C12.2364 13.5721 12.0132 13.5721 11.818 13.4846L6.69303 11.1872C6.31505 11.0177 6.146 10.574 6.31544 10.196Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M17.2498 4.09762C17.664 4.09762 17.9998 4.4334 17.9998 4.84762L17.9998 10.5028C17.9998 10.917 17.664 11.2528 17.2498 11.2528C16.8356 11.2528 16.4998 10.917 16.4998 10.5028V4.84762C16.4998 4.4334 16.8356 4.09762 17.2498 4.09762Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.99982 4.09762C7.41403 4.09762 7.74982 4.4334 7.74982 4.84762V10.5028C7.74982 10.917 7.41403 11.2528 6.99982 11.2528C6.58561 11.2528 6.24982 10.917 6.24982 10.5028V4.84762C6.24982 4.4334 6.58561 4.09762 6.99982 4.09762Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.1248 6.57175C12.539 6.57175 12.8748 6.90754 12.8748 7.32175V12.6235C12.8748 13.0377 12.539 13.3735 12.1248 13.3735C11.7106 13.3735 11.3748 13.0377 11.3748 12.6235V7.32175C11.3748 6.90754 11.7106 6.57175 12.1248 6.57175Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16.818 9.86582C17.0132 9.77833 17.2364 9.77833 17.4316 9.86582L22.5566 12.1632C22.8263 12.2841 22.9998 12.5521 22.9998 12.8476C22.9998 13.1431 22.8263 13.4111 22.5566 13.532L17.4316 15.8294C17.2364 15.9169 17.0132 15.9169 16.818 15.8294L11.693 13.532C11.4234 13.4111 11.2498 13.1431 11.2498 12.8476C11.2498 12.5521 11.4234 12.2841 11.693 12.1632L16.818 9.86582ZM13.8333 12.8476L17.1248 14.3231L20.4163 12.8476L17.1248 11.3721L13.8333 12.8476Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.3154 18.196C11.4849 17.818 11.9286 17.649 12.3066 17.8184L17.1248 19.9783L21.943 17.8184C22.321 17.649 22.7648 17.818 22.9342 18.196C23.1036 18.574 22.9346 19.0177 22.5566 19.1872L17.4316 21.4846C17.2364 21.5721 17.0132 21.5721 16.818 21.4846L11.693 19.1872C11.3151 19.0177 11.146 18.574 11.3154 18.196Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M22.2498 12.0976C22.664 12.0976 22.9998 12.4334 22.9998 12.8476L22.9998 18.5028C22.9998 18.917 22.664 19.2528 22.2498 19.2528C21.8356 19.2528 21.4998 18.917 21.4998 18.5028V12.8476C21.4998 12.4334 21.8356 12.0976 22.2498 12.0976Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.9998 12.0976C12.414 12.0976 12.7498 12.4334 12.7498 12.8476V18.5028C12.7498 18.917 12.414 19.2528 11.9998 19.2528C11.5856 19.2528 11.2498 18.917 11.2498 18.5028V12.8476C11.2498 12.4334 11.5856 12.0976 11.9998 12.0976Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M17.1248 14.5718C17.539 14.5718 17.8748 14.9075 17.8748 15.3218V20.6235C17.8748 21.0377 17.539 21.3735 17.1248 21.3735C16.7106 21.3735 16.3748 21.0377 16.3748 20.6235V15.3218C16.3748 14.9075 16.7106 14.5718 17.1248 14.5718Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.81803 9.86582C7.0132 9.77833 7.23644 9.77833 7.43161 9.86582L12.5566 12.1632C12.8263 12.2841 12.9998 12.5521 12.9998 12.8476C12.9998 13.1431 12.8263 13.4111 12.5566 13.532L7.43161 15.8294C7.23644 15.9169 7.0132 15.9169 6.81803 15.8294L1.69303 13.532C1.42336 13.4111 1.24982 13.1431 1.24982 12.8476C1.24982 12.5521 1.42336 12.2841 1.69303 12.1632L6.81803 9.86582ZM3.83331 12.8476L7.12482 14.3231L10.4163 12.8476L7.12482 11.3721L3.83331 12.8476Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.31544 18.196C1.48487 17.818 1.92864 17.649 2.30661 17.8184L7.12482 19.9783L11.943 17.8184C12.321 17.649 12.7648 17.818 12.9342 18.196C13.1036 18.574 12.9346 19.0177 12.5566 19.1872L7.43161 21.4846C7.23644 21.5721 7.0132 21.5721 6.81803 21.4846L1.69303 19.1872C1.31505 19.0177 1.146 18.574 1.31544 18.196Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.2498 12.0976C12.664 12.0976 12.9998 12.4334 12.9998 12.8476V18.5028C12.9998 18.917 12.664 19.2528 12.2498 19.2528C11.8356 19.2528 11.4998 18.917 11.4998 18.5028V12.8476C11.4998 12.4334 11.8356 12.0976 12.2498 12.0976Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.99982 12.0976C2.41403 12.0976 2.74982 12.4334 2.74982 12.8476V18.5028C2.74982 18.917 2.41403 19.2528 1.99982 19.2528C1.58561 19.2528 1.24982 18.917 1.24982 18.5028V12.8476C1.24982 12.4334 1.58561 12.0976 1.99982 12.0976Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.12482 14.5718C7.53903 14.5718 7.87482 14.9075 7.87482 15.3218V20.6235C7.87482 21.0377 7.53903 21.3735 7.12482 21.3735C6.71061 21.3735 6.37482 21.0377 6.37482 20.6235V15.3218C6.37482 14.9075 6.71061 14.5718 7.12482 14.5718Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.1666 4C10.4427 4 10.6666 4.22386 10.6666 4.5V11.5C10.6666 11.7761 10.4427 12 10.1666 12C9.89044 12 9.66659 11.7761 9.66659 11.5V4.5C9.66659 4.22386 9.89044 4 10.1666 4Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.83325 4C6.10939 4 6.33325 4.22386 6.33325 4.5V11.5C6.33325 11.7761 6.10939 12 5.83325 12C5.55711 12 5.33325 11.7761 5.33325 11.5V4.5C5.33325 4.22386 5.55711 4 5.83325 4Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,13 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.57046 3.408C3.71787 3.3169 3.90194 3.30862 4.05694 3.38612L12.3903 7.55278C12.5597 7.63748 12.6667 7.81061 12.6667 8C12.6667 8.18938 12.5597 8.36251 12.3903 8.44721L4.05694 12.6139C3.90194 12.6914 3.71787 12.6831 3.57046 12.592C3.42305 12.5009 3.33333 12.34 3.33333 12.1667V3.83333C3.33333 3.66004 3.42305 3.49911 3.57046 3.408ZM4.33333 4.64235V11.3576L11.0486 8L4.33333 4.64235Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.25 5.75C8.66015 5.75 5.75 8.66015 5.75 12.25C5.75 15.8399 8.66015 18.75 12.25 18.75C15.8399 18.75 18.75 15.8399 18.75 12.25C18.75 8.66015 15.8399 5.75 12.25 5.75ZM4.25 12.25C4.25 7.83172 7.83172 4.25 12.25 4.25C16.6683 4.25 20.25 7.83172 20.25 12.25C20.25 16.6683 16.6683 20.25 12.25 20.25C7.83172 20.25 4.25 16.6683 4.25 12.25Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.5303 8.46967C14.8232 8.76256 14.8232 9.23744 14.5303 9.53033L10.5303 13.5303C10.2374 13.8232 9.76256 13.8232 9.46967 13.5303C9.17678 13.2374 9.17678 12.7626 9.46967 12.4697L13.4697 8.46967C13.7626 8.17678 14.2374 8.17678 14.5303 8.46967Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.25 13C5.25 12.5858 5.58579 12.25 6 12.25H18.5C18.9142 12.25 19.25 12.5858 19.25 13C19.25 13.4142 18.9142 13.75 18.5 13.75H6C5.58579 13.75 5.25 13.4142 5.25 13Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 5.75C4 4.78351 4.78351 4 5.75 4H9.25C10.2165 4 11 4.78351 11 5.75V18.2502C11 19.2167 10.2165 20.0002 9.25 20.0002H5.75C4.78352 20.0002 4 19.2167 4 18.2502V5.75ZM5.75 5.5C5.61193 5.5 5.5 5.61193 5.5 5.75V18.2502C5.5 18.3883 5.61192 18.5002 5.75 18.5002H9.25C9.38808 18.5002 9.5 18.3883 9.5 18.2502V5.75C9.5 5.61193 9.38807 5.5 9.25 5.5H5.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13 5.75C13 4.78352 13.7835 4 14.75 4H18.25C19.2165 4 20 4.78352 20 5.75V9.25025C20 10.2168 19.2165 11.0002 18.25 11.0002H14.75C13.7835 11.0002 13 10.2168 13 9.25025V5.75ZM14.75 5.5C14.6119 5.5 14.5 5.61192 14.5 5.75V9.25025C14.5 9.38828 14.6119 9.5002 14.75 9.5002H18.25C18.3881 9.5002 18.5 9.38828 18.5 9.25025V5.75C18.5 5.61192 18.3881 5.5 18.25 5.5H14.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13 14.75C13 13.7835 13.7835 13 14.75 13H18.25C19.2165 13 20 13.7835 20 14.75V18.2502C20 19.2167 19.2165 20.0002 18.25 20.0002H14.75C13.7835 20.0002 13 19.2167 13 18.2502V14.75ZM14.75 14.5C14.6119 14.5 14.5 14.6119 14.5 14.75V18.2502C14.5 18.3883 14.6119 18.5002 14.75 18.5002H18.25C18.3881 18.5002 18.5 18.3883 18.5 18.2502V14.75C18.5 14.6119 18.3881 14.5 18.25 14.5H14.75Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.5126 5.17677C15.196 4.49332 16.3041 4.49332 16.9875 5.17677L18.8232 7.01253C19.5066 7.69595 19.5066 8.80397 18.8233 9.48739L9.53034 18.7803C9.43172 18.8789 9.30756 18.9481 9.17179 18.98L4.92179 19.98C4.66896 20.0395 4.40335 19.964 4.21968 19.7803C4.03602 19.5966 3.96046 19.331 4.01995 19.0782L5.01995 14.8282C5.0519 14.6924 5.12106 14.5683 5.21969 14.4696L14.5126 5.17677ZM15.9268 6.23739C15.8292 6.13978 15.6709 6.13978 15.5733 6.23739L6.43121 15.3794L5.75757 18.2424L8.62055 17.5688L17.7626 8.42677C17.7626 8.42678 17.7626 8.42677 17.7626 8.42677C17.8602 8.32913 17.8602 8.17081 17.7626 8.07317L15.9268 6.23739Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13 19.25C13 18.8358 13.3358 18.5 13.75 18.5H19.25C19.6642 18.5 20 18.8358 20 19.25C20 19.6642 19.6642 20 19.25 20H13.75C13.3358 20 13 19.6642 13 19.25Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.99996 3.66669C5.60673 3.66669 3.66663 5.60679 3.66663 8.00002C3.66663 10.3933 5.60673 12.3334 7.99996 12.3334C10.3932 12.3334 12.3333 10.3933 12.3333 8.00002C12.3333 5.60679 10.3932 3.66669 7.99996 3.66669ZM2.66663 8.00002C2.66663 5.0545 5.05444 2.66669 7.99996 2.66669C10.9455 2.66669 13.3333 5.0545 13.3333 8.00002C13.3333 10.9455 10.9455 13.3334 7.99996 13.3334C5.05444 13.3334 2.66663 10.9455 2.66663 8.00002Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.04854 4.69103C6.65219 5.44491 6.33329 6.57193 6.33329 8.00002C6.33329 9.42811 6.65219 10.5551 7.04854 11.309C7.2474 11.6873 7.459 11.9589 7.64726 12.1291C7.8457 12.3085 7.96937 12.3334 7.99996 12.3334C8.03055 12.3334 8.15422 12.3085 8.35266 12.1291C8.54092 11.9589 8.75252 11.6873 8.95138 11.309C9.34773 10.5551 9.66663 9.42811 9.66663 8.00002C9.66663 6.57193 9.34773 5.44491 8.95138 4.69103C8.75252 4.31278 8.54092 4.04112 8.35266 3.87091C8.15422 3.6915 8.03055 3.66669 7.99996 3.66669C7.96937 3.66669 7.8457 3.6915 7.64726 3.87091C7.459 4.04112 7.2474 4.31278 7.04854 4.69103ZM6.97662 3.12913C7.26209 2.87104 7.61635 2.66669 7.99996 2.66669C8.38357 2.66669 8.73783 2.87104 9.0233 3.12913C9.31895 3.39642 9.59725 3.7706 9.83651 4.22568C10.3164 5.13846 10.6666 6.42811 10.6666 8.00002C10.6666 9.57193 10.3164 10.8616 9.83651 11.7744C9.59725 12.2294 9.31895 12.6036 9.0233 12.8709C8.73783 13.129 8.38357 13.3334 7.99996 13.3334C7.61635 13.3334 7.26209 13.129 6.97662 12.8709C6.68096 12.6036 6.40267 12.2294 6.16341 11.7744C5.68353 10.8616 5.33329 9.57193 5.33329 8.00002C5.33329 6.42811 5.68353 5.13846 6.16341 4.22568C6.40267 3.7706 6.68096 3.39642 6.97662 3.12913Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.83329 8.00002C2.83329 7.72388 3.05715 7.50002 3.33329 7.50002H12.6666C12.9428 7.50002 13.1666 7.72388 13.1666 8.00002C13.1666 8.27616 12.9428 8.50002 12.6666 8.50002H3.33329C3.05715 8.50002 2.83329 8.27616 2.83329 8.00002Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.2047 4.23503C14.4891 3.9339 14.9638 3.92033 15.265 4.20474L19.765 8.45474C19.915 8.59642 20 8.79366 20 9C20 9.20635 19.915 9.40358 19.765 9.54526L15.265 13.7953C14.9638 14.0797 14.4891 14.0661 14.2047 13.765C13.9203 13.4638 13.9339 12.9892 14.235 12.7047L18.1577 9L14.235 5.29526C13.9339 5.01085 13.9203 4.53617 14.2047 4.23503Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 13C4 10.3767 6.12664 8.25 8.75 8.25H19.25C19.6642 8.25 20 8.58579 20 9C20 9.41422 19.6642 9.75 19.25 9.75H8.75C6.95508 9.75 5.5 11.2051 5.5 13V19.25C5.5 19.6642 5.16421 20 4.75 20C4.33579 20 4 19.6642 4 19.25V13Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,55 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.50002 3.66669C4.03978 3.66669 3.66669 4.03978 3.66669 4.50002V11.5C3.66669 11.9603 4.03978 12.3334 4.50002 12.3334H11.5C11.9603 12.3334 12.3334 11.9603 12.3334 11.5V4.50002C12.3334 4.03978 11.9603 3.66669 11.5 3.66669H4.50002ZM2.66669 4.50002C2.66669 3.4875 3.4875 2.66669 4.50002 2.66669H11.5C12.5126 2.66669 13.3334 3.4875 13.3334 4.50002V11.5C13.3334 12.5126 12.5126 13.3334 11.5 13.3334H4.50002C3.4875 13.3334 2.66669 12.5126 2.66669 11.5V4.50002Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.16669 2.83335C5.44283 2.83335 5.66669 3.05721 5.66669 3.33335V12.6667C5.66669 12.9428 5.44283 13.1667 5.16669 13.1667C4.89054 13.1667 4.66669 12.9428 4.66669 12.6667V3.33335C4.66669 3.05721 4.89054 2.83335 5.16669 2.83335Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.8334 2.83335C11.1095 2.83335 11.3334 3.05721 11.3334 3.33335V12.6667C11.3334 12.9428 11.1095 13.1667 10.8334 13.1667C10.5572 13.1667 10.3334 12.9428 10.3334 12.6667V3.33335C10.3334 3.05721 10.5572 2.83335 10.8334 2.83335Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.83335 5.83335C2.83335 5.55721 3.05721 5.33335 3.33335 5.33335H5.00002C5.27616 5.33335 5.50002 5.55721 5.50002 5.83335C5.50002 6.1095 5.27616 6.33335 5.00002 6.33335H3.33335C3.05721 6.33335 2.83335 6.1095 2.83335 5.83335Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.8334 5.83335C10.8334 5.55721 11.0572 5.33335 11.3334 5.33335H12.6667C12.9428 5.33335 13.1667 5.55721 13.1667 5.83335C13.1667 6.1095 12.9428 6.33335 12.6667 6.33335H11.3334C11.0572 6.33335 10.8334 6.1095 10.8334 5.83335Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.83335 8.00002C2.83335 7.72388 3.05721 7.50002 3.33335 7.50002H12.6667C12.9428 7.50002 13.1667 7.72388 13.1667 8.00002C13.1667 8.27616 12.9428 8.50002 12.6667 8.50002H3.33335C3.05721 8.50002 2.83335 8.27616 2.83335 8.00002Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.83335 10.1667C2.83335 9.89054 3.05721 9.66669 3.33335 9.66669H5.00002C5.27616 9.66669 5.50002 9.89054 5.50002 10.1667C5.50002 10.4428 5.27616 10.6667 5.00002 10.6667H3.33335C3.05721 10.6667 2.83335 10.4428 2.83335 10.1667Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.8334 10.1667C10.8334 9.89054 11.0572 9.66669 11.3334 9.66669H12.6667C12.9428 9.66669 13.1667 9.89054 13.1667 10.1667C13.1667 10.4428 12.9428 10.6667 12.6667 10.6667H11.3334C11.0572 10.6667 10.8334 10.4428 10.8334 10.1667Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M8.66668 8.00002C8.66668 8.36822 8.36821 8.66669 8.00001 8.66669C7.63181 8.66669 7.33334 8.36822 7.33334 8.00002C7.33334 7.63182 7.63181 7.33335 8.00001 7.33335C8.36821 7.33335 8.66668 7.63182 8.66668 8.00002Z"
fill="currentColor"
/>
<path
d="M8.66668 5.33335C8.66668 5.70154 8.36821 6.00002 8.00001 6.00002C7.63181 6.00002 7.33334 5.70154 7.33334 5.33335C7.33334 4.96517 7.63181 4.66669 8.00001 4.66669C8.36821 4.66669 8.66668 4.96517 8.66668 5.33335Z"
fill="currentColor"
/>
<path
d="M8.66668 10.6667C8.66668 11.0349 8.36821 11.3334 8.00001 11.3334C7.63181 11.3334 7.33334 11.0349 7.33334 10.6667C7.33334 10.2985 7.63181 10 8.00001 10C8.36821 10 8.66668 10.2985 8.66668 10.6667Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 5.5C7.96243 5.5 5.5 7.96243 5.5 11C5.5 14.0376 7.96243 16.5 11 16.5C14.0376 16.5 16.5 14.0376 16.5 11C16.5 7.96243 14.0376 5.5 11 5.5ZM4 11C4 7.13401 7.13401 4 11 4C14.866 4 18 7.13401 18 11C18 14.866 14.866 18 11 18C7.13401 18 4 14.866 4 11Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.9697 14.9697C15.2626 14.6768 15.7374 14.6768 16.0303 14.9697L19.7803 18.7197C20.0732 19.0126 20.0732 19.4874 19.7803 19.7803C19.4874 20.0732 19.0126 20.0732 18.7197 19.7803L14.9697 16.0303C14.6768 15.7374 14.6768 15.2626 14.9697 14.9697Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,25 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M17.315 7.00282C17.7276 7.0387 18.0331 7.40232 17.9972 7.81498L17.156 17.4882C17.156 17.4882 17.156 17.4882 17.156 17.4882C17.0325 18.9093 15.8428 20 14.4164 20H9.58363C8.15721 20 6.96754 18.9094 6.84397 17.4883L6.00282 7.81497C5.96694 7.40232 6.27237 7.0387 6.68503 7.00282C7.09768 6.96694 7.4613 7.27237 7.49718 7.68503L8.33833 17.3583C8.3945 18.0042 8.93523 18.5 9.58363 18.5H14.4164C15.0648 18.5 15.6055 18.0042 15.6616 17.3584L16.5028 7.68502C16.5387 7.27237 16.9023 6.96693 17.315 7.00282Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.75 5.5C11.0596 5.5 10.5 6.05964 10.5 6.75V7.5C10.5 7.91421 10.1642 8.25 9.75 8.25C9.33579 8.25 9 7.91421 9 7.5V6.75C9 5.23122 10.2312 4 11.75 4H12.25C13.7688 4 15 5.23122 15 6.75V7.5C15 7.91421 14.6642 8.25 14.25 8.25C13.8358 8.25 13.5 7.91421 13.5 7.5V6.75C13.5 6.05964 12.9404 5.5 12.25 5.5H11.75Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.25 7.75C4.25 7.33579 4.58579 7 5 7H19C19.4142 7 19.75 7.33579 19.75 7.75C19.75 8.16421 19.4142 8.5 19 8.5H5C4.58579 8.5 4.25 8.16421 4.25 7.75Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,19 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.79526 4.23503C10.0797 4.53617 10.0661 5.01085 9.76497 5.29526L5.8423 9L9.76497 12.7047C10.0661 12.9892 10.0797 13.4638 9.79526 13.765C9.51085 14.0661 9.03617 14.0797 8.73503 13.7953L4.23503 9.54526C4.08502 9.40358 4 9.20635 4 9C4 8.79366 4.08502 8.59642 4.23503 8.45474L8.73503 4.20474C9.03617 3.92033 9.51085 3.9339 9.79526 4.23503Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.75 9C4.75 8.58579 5.08579 8.25 5.5 8.25H15.25C17.8733 8.25 20 10.3767 20 13V19.25C20 19.6642 19.6642 20 19.25 20C18.8358 20 18.5 19.6642 18.5 19.25V13C18.5 11.2051 17.0449 9.75 15.25 9.75H5.5C5.08579 9.75 4.75 9.41422 4.75 9Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,31 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.87161 2.83221C8.05634 3.03746 8.0397 3.35361 7.83444 3.53834L6.58072 4.66669L7.83444 5.79504C8.0397 5.97977 8.05634 6.29592 7.87161 6.50117C7.68688 6.70643 7.37073 6.72307 7.16548 6.53834L5.49881 5.03834C5.39345 4.94352 5.33329 4.80843 5.33329 4.66669C5.33329 4.52495 5.39345 4.38987 5.49881 4.29504L7.16548 2.79504C7.37073 2.61031 7.68688 2.62695 7.87161 2.83221Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.99996 4.66669C5.99996 4.39055 6.22382 4.16669 6.49996 4.16669H8.83329C11.3186 4.16669 13.3333 6.18141 13.3333 8.66669V8.83336C13.3333 9.1095 13.1094 9.33336 12.8333 9.33336C12.5572 9.33336 12.3333 9.1095 12.3333 8.83336V8.66669C12.3333 6.73369 10.7663 5.16669 8.83329 5.16669H6.49996C6.22382 5.16669 5.99996 4.94283 5.99996 4.66669Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.12831 9.49888C8.31304 9.29362 8.62919 9.27698 8.83444 9.46171L10.5011 10.9617C10.6065 11.0565 10.6666 11.1916 10.6666 11.3334C10.6666 11.4751 10.6065 11.6102 10.5011 11.705L8.83444 13.205C8.62919 13.3897 8.31304 13.3731 8.12831 13.1678C7.94358 12.9626 7.96022 12.6464 8.16548 12.4617L9.4192 11.3334L8.16548 10.205C7.96022 10.0203 7.94358 9.70413 8.12831 9.49888Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.16663 6.66669C3.44277 6.66669 3.66663 6.89055 3.66663 7.16669V7.33336C3.66663 9.26635 5.23363 10.8334 7.16663 10.8334H9.49996C9.7761 10.8334 9.99996 11.0572 9.99996 11.3334C9.99996 11.6095 9.7761 11.8334 9.49996 11.8334H7.16663C4.68134 11.8334 2.66663 9.81863 2.66663 7.33336V7.16669C2.66663 6.89055 2.89048 6.66669 3.16663 6.66669Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -1,31 +0,0 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon {...props} width="24" height="24" viewBox="0 0 24 24" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 5.5C7.96243 5.5 5.5 7.96243 5.5 11C5.5 14.0376 7.96243 16.5 11 16.5C14.0376 16.5 16.5 14.0376 16.5 11C16.5 7.96243 14.0376 5.5 11 5.5ZM4 11C4 7.13401 7.13401 4 11 4C14.866 4 18 7.13401 18 11C18 14.866 14.866 18 11 18C7.13401 18 4 14.866 4 11Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.9697 14.9697C15.2626 14.6768 15.7374 14.6768 16.0303 14.9697L19.7803 18.7197C20.0732 19.0126 20.0732 19.4874 19.7803 19.7803C19.4874 20.0732 19.0126 20.0732 18.7197 19.7803L14.9697 16.0303C14.6768 15.7374 14.6768 15.2626 14.9697 14.9697Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 8C11.4142 8 11.75 8.33579 11.75 8.75V13.25C11.75 13.6642 11.4142 14 11 14C10.5858 14 10.25 13.6642 10.25 13.25V8.75C10.25 8.33579 10.5858 8 11 8Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 11C8 10.5858 8.33579 10.25 8.75 10.25H13.25C13.6642 10.25 14 10.5858 14 11C14 11.4142 13.6642 11.75 13.25 11.75H8.75C8.33579 11.75 8 11.4142 8 11Z"
fill="currentColor"
/>
</SvgIcon>
));

View File

@@ -18,42 +18,6 @@ import Cross from './CustomSvgIcons/Cross';
import IconButton from './IconButton';
import { Line } from './Grid';
import GDevelopThemeContext from './Theme/ThemeContext';
import optionalRequire from '../Utils/OptionalRequire';
import useForceUpdate from '../Utils/UseForceUpdate';
import { useWindowControlsOverlayWatcher } from '../Utils/Window';
const electron = optionalRequire('electron');
const DRAGGABLE_PART_CLASS_NAME = 'title-bar-draggable-part';
export const DialogTitleBar = ({
backgroundColor,
}: {|
backgroundColor: string,
|}) => {
// An installed PWA can have window controls displayed as overlay. If supported,
// we set up a listener to detect any change and force a refresh that will read
// the latest size of the controls.
const forceUpdate = useForceUpdate();
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
// $FlowFixMe - this API is not handled by Flow.
const { windowControlsOverlay } = navigator;
if (!!electron || (windowControlsOverlay && windowControlsOverlay.visible)) {
// We're on the desktop app, or in an installed PWA with window controls displayed
// as overlay: we need to display a spacing at the top of the dialog.
return (
<div
className={DRAGGABLE_PART_CLASS_NAME}
style={{ height: 38, backgroundColor, flexShrink: 0 }}
/>
);
}
// Not on the desktop app, and not in an installed PWA with window controls displayed
// as overlay: no need to display a spacing.
return null;
};
// Default.
const dialogPaddingX = 24;
@@ -218,7 +182,6 @@ const Dialog = ({
const hasActions =
(actions && actions.filter(Boolean).length > 0) ||
(secondaryActions && secondaryActions.filter(Boolean).length > 0);
const isFullScreen = size === 'small' && !noMobileFullScreen;
const classesForDangerousDialog = useDangerousStylesForDialog();
const classesForDialogContent = useStylesForDialogContent();
@@ -259,9 +222,10 @@ const Dialog = ({
const dialogContainerStyle = {
...styles.dialogContainer,
// Ensure we don't spread an object here, to avoid a styling bug when resizing.
margin: isFullScreen
? dialogSmallPadding
: `${dialogTitlePadding}px ${dialogPaddingX}px ${dialogActionPadding}px ${dialogPaddingX}px`,
margin:
size === 'small'
? dialogSmallPadding
: `${dialogTitlePadding}px ${dialogPaddingX}px ${dialogActionPadding}px ${dialogPaddingX}px`,
};
const onCloseDialog = React.useCallback(
@@ -310,9 +274,9 @@ const Dialog = ({
open={open}
onClose={onCloseDialog}
fullWidth
fullScreen={isFullScreen}
fullScreen={size === 'small' && !noMobileFullScreen}
className={classNames({
'safe-area-aware-container': isFullScreen,
'safe-area-aware-container': size === 'small',
})}
PaperProps={{
id,
@@ -325,11 +289,6 @@ const Dialog = ({
disableBackdropClick={false}
onKeyDown={handleKeyDown}
>
{isFullScreen && (
<DialogTitleBar
backgroundColor={gdevelopTheme.titlebar.backgroundColor}
/>
)}
<div style={dialogContainerStyle}>
{title && (
<div style={styles.titleContainer}>

View File

@@ -6,13 +6,14 @@ import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import Close from '@material-ui/icons/Close';
import Tooltip from '@material-ui/core/Tooltip';
import { tooltipEnterDelay } from './Tooltip';
import { DialogTitleBar } from '../UI/Dialog';
const appBarHeight = 32;
type Props = {|
title: React.Node,
displayRightCloseButton?: boolean,
onClose: () => void,
|};
@@ -38,25 +39,25 @@ const styles = {
const DrawerTopBar = (props: Props) => {
return (
<>
<DialogTitleBar backgroundColor="transparent" />
<AppBar
position="static"
style={styles.appBar}
className="safe-area-aware-top-margin"
color="primary"
elevation={0}
>
<Toolbar style={styles.toolbar}>
<Tooltip
title={props.title}
placement="bottom"
enterDelay={tooltipEnterDelay}
>
<Typography variant="h6" style={styles.title}>
{props.title}
</Typography>
</Tooltip>
<AppBar
position="static"
style={styles.appBar}
className="safe-area-aware-top-margin"
color="primary"
elevation={0}
>
<Toolbar style={styles.toolbar}>
<Tooltip
title={props.title}
placement="bottom"
enterDelay={tooltipEnterDelay}
>
<Typography variant="h6" style={styles.title}>
{props.title}
</Typography>
</Tooltip>
{props.displayRightCloseButton && (
<IconButton
onClick={props.onClose}
edge="end"
@@ -65,9 +66,9 @@ const DrawerTopBar = (props: Props) => {
>
<Close />
</IconButton>
</Toolbar>
</AppBar>
</>
)}
</Toolbar>
</AppBar>
);
};

View File

@@ -1,105 +0,0 @@
// @flow
import * as React from 'react';
import ElementWithMenu from './Menu/ElementWithMenu';
import { type MenuItemTemplate } from './Menu/Menu.flow';
import { type I18n as I18nType } from '@lingui/core';
import ButtonGroup from '@material-ui/core/ButtonGroup';
import Button from '@material-ui/core/Button';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import { Spacer } from './Grid';
type Props = {|
id?: string,
label?: React.Node,
primary?: boolean,
disabled?: boolean,
icon?: React.Node,
onClick: ?() => void,
buildMenuTemplate: (i18n: I18nType) => Array<MenuItemTemplate>,
style?: {|
marginTop?: number,
marginBottom?: number,
marginLeft?: number,
marginRight?: number,
margin?: number,
flexShrink?: 0,
|},
|};
const shouldNeverBeCalled = () => {
throw new Error(
'This FlatButtonWithSplitMenu onClick should never be called'
);
};
const styles = {
mainButton: { flex: 1 },
arrowDropDownButton: {
// Reduce the size forced by Material UI to avoid making the arrow
// too big.
minWidth: 30,
paddingLeft: 0,
paddingRight: 0,
},
};
/**
* A flat button based on Material-UI button, that has a menu displayed
* when the dropdown arrow is clicked.
*/
const FlatButtonWithSplitMenu = (props: Props) => {
const {
id,
buildMenuTemplate,
onClick,
label,
primary,
icon,
disabled,
} = props;
// In theory, focus ripple is only shown after a keyboard interaction
// (see https://github.com/mui-org/material-ui/issues/12067). However, as
// it's important to get focus right in the whole app, make the ripple
// always visible to be sure we're getting focusing right.
const focusRipple = true;
return (
<ButtonGroup
variant={'outlined'}
disableElevation
color={primary ? 'secondary' : 'default'}
disabled={disabled}
size="small"
style={props.style}
>
<Button
id={id}
focusRipple={focusRipple}
onClick={onClick}
style={styles.mainButton}
>
{icon}
{!!icon && !!label && <Spacer />}
{label}
</Button>
<ElementWithMenu
passExtraProps={
true /* ButtonGroup is passing props to Button: disabled, color, variant, size */
}
element={
<Button
onClick={shouldNeverBeCalled}
focusRipple={focusRipple}
style={styles.arrowDropDownButton}
>
<ArrowDropDownIcon />
</Button>
}
buildMenuTemplate={buildMenuTemplate}
/>
</ButtonGroup>
);
};
export default FlatButtonWithSplitMenu;

Some files were not shown because too many files have changed in this diff Show More