mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
2 Commits
feature/im
...
return-ful
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9995c5ee82 | ||
![]() |
3bebc57c88 |
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
|
@@ -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 |
@@ -25,7 +25,6 @@
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"display_override": ["window-controls-overlay"],
|
||||
"theme_color": "#252525",
|
||||
"background_color": "#f0f0f0"
|
||||
}
|
||||
|
@@ -225,6 +225,11 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"header": {
|
||||
"background-color": {
|
||||
"value": "{theme.surface.canvas.background-color.value}"
|
||||
}
|
||||
},
|
||||
"separator": {
|
||||
"color": {
|
||||
"value": "#32323B"
|
||||
|
@@ -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()}
|
||||
|
@@ -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;
|
||||
|
@@ -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 & 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>
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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) => {
|
||||
|
@@ -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) => [
|
||||
|
@@ -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) => [
|
||||
|
@@ -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) => [
|
||||
|
@@ -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) => [
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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(
|
||||
|
@@ -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, '-')}`;
|
||||
|
@@ -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, '-')
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
</>
|
||||
|
@@ -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) => [
|
||||
|
@@ -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) => [
|
||||
|
@@ -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;
|
@@ -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>
|
||||
|
@@ -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;
|
@@ -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;
|
||||
|
||||
|
@@ -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) => [
|
||||
|
@@ -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} />
|
||||
)}
|
||||
|
@@ -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`You’re 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>
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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}
|
||||
|
@@ -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(
|
||||
() =>
|
||||
|
@@ -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);
|
||||
|
30
newIDE/app/src/MainFrame/MainMenu.flow.js
Normal file
30
newIDE/app/src/MainFrame/MainMenu.flow.js
Normal 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>,
|
||||
|};
|
@@ -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);
|
||||
};
|
@@ -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,
|
||||
]
|
||||
);
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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() : ''
|
||||
|
@@ -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();
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -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}
|
||||
/>
|
||||
|
@@ -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 = {|
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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) => {
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
</>
|
||||
|
@@ -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');
|
||||
}
|
||||
};
|
||||
|
@@ -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,
|
||||
|
@@ -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}
|
||||
/>,
|
||||
]}
|
||||
|
@@ -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}
|
||||
|
@@ -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}
|
||||
|
@@ -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"
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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>
|
||||
));
|
@@ -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}>
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -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
Reference in New Issue
Block a user