Skip to content
Snippets Groups Projects
Select Git revision
  • cfb55514b5ca165195a3223161b361c0fc332edb
  • develop default
  • gh-pages
  • frontend-logging
  • feature-sb3-save-restore
  • logging
  • default_testing
  • dependabot/npm_and_yarn/style-loader-1.2.1
  • dependabot/npm_and_yarn/eslint-7.5.0
  • dependabot/npm_and_yarn/js-base64-3.2.4
  • master
  • dependabot/npm_and_yarn/chromedriver-84.0.0
  • hotfix/rever-render-version
  • dependabot/npm_and_yarn/scratch-render-0.1.0-prerelease.20200716193608
  • dependabot/npm_and_yarn/jest-26.1.0
  • dependabot/npm_and_yarn/eslint-plugin-jest-23.18.0
  • dependabot/npm_and_yarn/react-ga-3.1.2
  • dependabot/npm_and_yarn/copy-webpack-plugin-6.0.3
  • dependabot/npm_and_yarn/eslint-plugin-react-7.20.3
  • circle-node-testing
  • dependabot/npm_and_yarn/bowser-2.10.0
  • 0.1.0-prerelease.20200720192905
  • 0.1.0-prerelease.20200720192719
  • 0.1.0-prerelease.20200717030138
  • 0.1.0-prerelease.20200716204544
  • 0.1.0-prerelease.20200716195156
  • 0.1.0-prerelease.20200716152052
  • 0.1.0-prerelease.20200716145944
  • 0.1.0-prerelease.20200715032543
  • 0.1.0-prerelease.20200716194917
  • 0.1.0-prerelease.20200714211936
  • 0.1.0-prerelease.20200714210708
  • 0.1.0-prerelease.20200714205751
  • 0.1.0-prerelease.20200714204339
  • 0.1.0-prerelease.20200714165737
  • 0.1.0-prerelease.20200714162202
  • 0.1.0-prerelease.20200714160943
  • 0.1.0-prerelease.20200711060440
  • 0.1.0-prerelease.20200709205146
  • 0.1.0-prerelease.20200709203105
  • 0.1.0-prerelease.20200709155647
41 results

gui.jsx

Blame
  • user avatar
    Christopher Willis-Ford authored
    Pass a `stageSize` property down from a centralized `MediaQuery` in the
    GUI component, and control the size of the stage and all surrounding
    elements based on that value. Note that this is a pure property and is
    not stored in the Redux state, since we gain access to the results of
    the media query during `render()` and we shouldn't change Redux state
    during `render()`.
    
    Some stage-adjacent elements don't yet react correctly to the smallest
    stage size.
    cfb55514
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    gui.jsx 12.77 KiB
    import classNames from 'classnames';
    import PropTypes from 'prop-types';
    import React from 'react';
    import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
    import {connect} from 'react-redux';
    import MediaQuery from 'react-responsive';
    import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
    import tabStyles from 'react-tabs/style/react-tabs.css';
    import VM from 'scratch-vm';
    import Renderer from 'scratch-render';
    
    import Blocks from '../../containers/blocks.jsx';
    import CostumeTab from '../../containers/costume-tab.jsx';
    import TargetPane from '../../containers/target-pane.jsx';
    import SoundTab from '../../containers/sound-tab.jsx';
    import StageWrapper from '../../containers/stage-wrapper.jsx';
    import Loader from '../loader/loader.jsx';
    import Box from '../box/box.jsx';
    import MenuBar from '../menu-bar/menu-bar.jsx';
    import CostumeLibrary from '../../containers/costume-library.jsx';
    import BackdropLibrary from '../../containers/backdrop-library.jsx';
    
    import Backpack from '../../containers/backpack.jsx';
    import PreviewModal from '../../containers/preview-modal.jsx';
    import ImportModal from '../../containers/import-modal.jsx';
    import WebGlModal from '../../containers/webgl-modal.jsx';
    import TipsLibrary from '../../containers/tips-library.jsx';
    import Cards from '../../containers/cards.jsx';
    import DragLayer from '../../containers/drag-layer.jsx';
    
    import layout, {STAGE_SIZES} from '../../lib/layout-constants';
    import {resolveStageSize} from '../../lib/screen-utils';
    
    import styles from './gui.css';
    import addExtensionIcon from './icon--extensions.svg';
    import codeIcon from './icon--code.svg';
    import costumesIcon from './icon--costumes.svg';
    import soundsIcon from './icon--sounds.svg';
    
    const messages = defineMessages({
        addExtension: {
            id: 'gui.gui.addExtension',
            description: 'Button to add an extension in the target pane',
            defaultMessage: 'Add Extension'
        }
    });
    
    // Cache this value to only retrieve it once the first time.
    // Assume that it doesn't change for a session.
    let isRendererSupported = null;
    
    const GUIComponent = props => {
        const {
            activeTabIndex,
            basePath,
            backdropLibraryVisible,
            backpackOptions,
            blocksTabVisible,
            cardsVisible,
            children,
            costumeLibraryVisible,
            costumesTabVisible,
            enableCommunity,
            importInfoVisible,
            intl,
            isPlayerOnly,
            loading,
            onExtensionButtonClick,
            onActivateCostumesTab,
            onActivateSoundsTab,
            onActivateTab,
            onRequestCloseBackdropLibrary,
            onRequestCloseCostumeLibrary,
            previewInfoVisible,
            targetIsStage,
            soundsTabVisible,
            stageSizeMode,
            tipsLibraryVisible,
            vm,
            ...componentProps
        } = props;
        if (children) {
            return <Box {...componentProps}>{children}</Box>;
        }
    
        const tabClassNames = {
            tabs: styles.tabs,
            tab: classNames(tabStyles.reactTabsTab, styles.tab),
            tabList: classNames(tabStyles.reactTabsTabList, styles.tabList),
            tabPanel: classNames(tabStyles.reactTabsTabPanel, styles.tabPanel),
            tabPanelSelected: classNames(tabStyles.reactTabsTabPanelSelected, styles.isSelected),
            tabSelected: classNames(tabStyles.reactTabsTabSelected, styles.isSelected)
        };
    
        if (isRendererSupported === null) {
            isRendererSupported = Renderer.isSupported();
        }
    
        return (<MediaQuery minWidth={layout.fullSizeMinWidth}>{isFullSize => {
            const stageSize = resolveStageSize(stageSizeMode, !isFullSize);
    
            return isPlayerOnly ? (
                <StageWrapper
                    isRendererSupported={isRendererSupported}
                    stageSize={stageSize}
                    vm={vm}
                />
            ) : (
                <Box
                    className={styles.pageWrapper}
                    {...componentProps}
                >
                    {previewInfoVisible ? (
                        <PreviewModal />
                    ) : null}
                    {loading ? (
                        <Loader />
                    ) : null}
                    {importInfoVisible ? (
                        <ImportModal />
                    ) : null}
                    {isRendererSupported ? null : (
                        <WebGlModal />
                    )}
                    {tipsLibraryVisible ? (
                        <TipsLibrary />
                    ) : null}
                    {cardsVisible ? (
                        <Cards />
                    ) : null}
                    {costumeLibraryVisible ? (
                        <CostumeLibrary
                            vm={vm}
                            onRequestClose={onRequestCloseCostumeLibrary}
                        />
                    ) : null}
                    {backdropLibraryVisible ? (
                        <BackdropLibrary
                            vm={vm}
                            onRequestClose={onRequestCloseBackdropLibrary}
                        />
                    ) : null}
                    <MenuBar enableCommunity={enableCommunity} />
                    <Box className={styles.bodyWrapper}>
                        <Box className={styles.flexWrapper}>
                            <Box className={styles.editorWrapper}>
                                <Tabs
                                    className={tabClassNames.tabs}
                                    forceRenderTabPanel={true} // eslint-disable-line react/jsx-boolean-value
                                    selectedIndex={activeTabIndex}
                                    selectedTabClassName={tabClassNames.tabSelected}
                                    selectedTabPanelClassName={tabClassNames.tabPanelSelected}
                                    onSelect={onActivateTab}
                                >
                                    <TabList className={tabClassNames.tabList}>
                                        <Tab className={tabClassNames.tab}>
                                            <img
                                                draggable={false}
                                                src={codeIcon}
                                            />
                                            <FormattedMessage
                                                defaultMessage="Code"
                                                description="Button to get to the code panel"
                                                id="gui.gui.codeTab"
                                            />
                                        </Tab>
                                        <Tab
                                            className={tabClassNames.tab}
                                            onClick={onActivateCostumesTab}
                                        >
                                            <img
                                                draggable={false}
                                                src={costumesIcon}
                                            />
                                            {targetIsStage ? (
                                                <FormattedMessage
                                                    defaultMessage="Backdrops"
                                                    description="Button to get to the backdrops panel"
                                                    id="gui.gui.backdropsTab"
                                                />
                                            ) : (
                                                <FormattedMessage
                                                    defaultMessage="Costumes"
                                                    description="Button to get to the costumes panel"
                                                    id="gui.gui.costumesTab"
                                                />
                                            )}
                                        </Tab>
                                        <Tab
                                            className={tabClassNames.tab}
                                            onClick={onActivateSoundsTab}
                                        >
                                            <img
                                                draggable={false}
                                                src={soundsIcon}
                                            />
                                            <FormattedMessage
                                                defaultMessage="Sounds"
                                                description="Button to get to the sounds panel"
                                                id="gui.gui.soundsTab"
                                            />
                                        </Tab>
                                    </TabList>
                                    <TabPanel className={tabClassNames.tabPanel}>
                                        <Box className={styles.blocksWrapper}>
                                            <Blocks
                                                grow={1}
                                                isVisible={blocksTabVisible}
                                                options={{
                                                    media: `${basePath}static/blocks-media/`
                                                }}
                                                vm={vm}
                                            />
                                        </Box>
                                        <Box className={styles.extensionButtonContainer}>
                                            <button
                                                className={styles.extensionButton}
                                                title={intl.formatMessage(messages.addExtension)}
                                                onClick={onExtensionButtonClick}
                                            >
                                                <img
                                                    className={styles.extensionButtonIcon}
                                                    draggable={false}
                                                    src={addExtensionIcon}
                                                />
                                            </button>
                                        </Box>
                                    </TabPanel>
                                    <TabPanel className={tabClassNames.tabPanel}>
                                        {costumesTabVisible ? <CostumeTab vm={vm} /> : null}
                                    </TabPanel>
                                    <TabPanel className={tabClassNames.tabPanel}>
                                        {soundsTabVisible ? <SoundTab vm={vm} /> : null}
                                    </TabPanel>
                                </Tabs>
                                {backpackOptions.visible ? (
                                    <Backpack host={backpackOptions.host} />
                                ) : null}
                            </Box>
    
                            <Box className={styles.stageAndTargetWrapper}>
                                <StageWrapper
                                    isRendererSupported={isRendererSupported}
                                    stageSize={stageSize}
                                    vm={vm}
                                />
                                <Box className={styles.targetWrapper}>
                                    <TargetPane vm={vm} />
                                </Box>
                            </Box>
                        </Box>
                    </Box>
                    <DragLayer />
                </Box>
            );
        }}</MediaQuery>);
    };
    
    GUIComponent.propTypes = {
        activeTabIndex: PropTypes.number,
        backdropLibraryVisible: PropTypes.bool,
        backpackOptions: PropTypes.shape({
            host: PropTypes.string,
            visible: PropTypes.bool
        }),
        basePath: PropTypes.string,
        blocksTabVisible: PropTypes.bool,
        cardsVisible: PropTypes.bool,
        children: PropTypes.node,
        costumeLibraryVisible: PropTypes.bool,
        costumesTabVisible: PropTypes.bool,
        enableCommunity: PropTypes.bool,
        importInfoVisible: PropTypes.bool,
        intl: intlShape.isRequired,
        isPlayerOnly: PropTypes.bool,
        loading: PropTypes.bool,
        onActivateCostumesTab: PropTypes.func,
        onActivateSoundsTab: PropTypes.func,
        onActivateTab: PropTypes.func,
        onExtensionButtonClick: PropTypes.func,
        onRequestCloseBackdropLibrary: PropTypes.func,
        onRequestCloseCostumeLibrary: PropTypes.func,
        onTabSelect: PropTypes.func,
        previewInfoVisible: PropTypes.bool,
        soundsTabVisible: PropTypes.bool,
        stageSizeMode: PropTypes.oneOf([STAGE_SIZES.large, STAGE_SIZES.small]),
        targetIsStage: PropTypes.bool,
        tipsLibraryVisible: PropTypes.bool,
        vm: PropTypes.instanceOf(VM).isRequired
    };
    GUIComponent.defaultProps = {
        backpackOptions: {
            host: null,
            visible: false
        },
        basePath: './',
        stageSizeMode: STAGE_SIZES.large
    };
    
    const mapStateToProps = state => ({
        // This is the button's mode, as opposed to the actual current state
        stageSizeMode: state.scratchGui.stageSize.stageSize
    });
    
    export default injectIntl(connect(
        mapStateToProps
    )(GUIComponent));