mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-08-10 21:19:43 -04:00
Merge pull request #5306 from paulkaplan/studio-info-styling
Studio info styling
This commit is contained in:
commit
4f54e14e63
9 changed files with 236 additions and 149 deletions
|
@ -13,7 +13,7 @@ const Status = keyMirror({
|
||||||
});
|
});
|
||||||
|
|
||||||
const getInitialState = () => ({
|
const getInitialState = () => ({
|
||||||
infoStatus: Status.NOT_FETCHED,
|
infoStatus: Status.FETCHING,
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
openToAll: false,
|
openToAll: false,
|
||||||
|
@ -38,12 +38,14 @@ const studioReducer = (state, action) => {
|
||||||
case 'SET_INFO':
|
case 'SET_INFO':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...action.info
|
...action.info,
|
||||||
|
infoStatus: Status.FETCHED
|
||||||
};
|
};
|
||||||
case 'SET_ROLES':
|
case 'SET_ROLES':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...action.roles
|
...action.roles,
|
||||||
|
rolesStatus: Status.FETCHED
|
||||||
};
|
};
|
||||||
case 'SET_FETCH_STATUS':
|
case 'SET_FETCH_STATUS':
|
||||||
if (action.error) {
|
if (action.error) {
|
||||||
|
@ -95,14 +97,12 @@ const selectIsFetchingRoles = state => state.studio.rolesStatus === Status.FETCH
|
||||||
|
|
||||||
// Thunks
|
// Thunks
|
||||||
const getInfo = () => ((dispatch, getState) => {
|
const getInfo = () => ((dispatch, getState) => {
|
||||||
dispatch(setFetchStatus('infoStatus', Status.FETCHING));
|
|
||||||
const studioId = selectStudioId(getState());
|
const studioId = selectStudioId(getState());
|
||||||
api({uri: `/studios/${studioId}`}, (err, body, res) => {
|
api({uri: `/studios/${studioId}`}, (err, body, res) => {
|
||||||
if (err || typeof body === 'undefined' || res.statusCode !== 200) {
|
if (err || typeof body === 'undefined' || res.statusCode !== 200) {
|
||||||
dispatch(setFetchStatus('infoStatus', Status.ERROR, err));
|
dispatch(setFetchStatus('infoStatus', Status.ERROR, err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(setFetchStatus('infoStatus', Status.FETCHED));
|
|
||||||
dispatch(setInfo({
|
dispatch(setInfo({
|
||||||
title: body.title,
|
title: body.title,
|
||||||
description: body.description,
|
description: body.description,
|
||||||
|
@ -130,7 +130,6 @@ const getRoles = () => ((dispatch, getState) => {
|
||||||
dispatch(setFetchStatus('rolesStatus', Status.ERROR, err));
|
dispatch(setFetchStatus('rolesStatus', Status.ERROR, err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(setFetchStatus('rolesStatus', Status.FETCHED));
|
|
||||||
dispatch(setRoles({
|
dispatch(setRoles({
|
||||||
manager: body.manager,
|
manager: body.manager,
|
||||||
curator: body.curator,
|
curator: body.curator,
|
||||||
|
|
|
@ -8,31 +8,29 @@ import {selectCanEditInfo} from '../../redux/studio-permissions';
|
||||||
import {
|
import {
|
||||||
mutateStudioDescription, selectIsMutatingDescription, selectDescriptionMutationError
|
mutateStudioDescription, selectIsMutatingDescription, selectDescriptionMutationError
|
||||||
} from '../../redux/studio-mutations';
|
} from '../../redux/studio-mutations';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const StudioDescription = ({
|
const StudioDescription = ({
|
||||||
descriptionError, isFetching, isMutating, description, canEditInfo, handleUpdate
|
descriptionError, isFetching, isMutating, description, canEditInfo, handleUpdate
|
||||||
}) => (
|
}) => {
|
||||||
<div>
|
const fieldClassName = classNames('studio-description', {
|
||||||
<h3>Description</h3>
|
'mod-fetching': isFetching,
|
||||||
{isFetching ? (
|
'mod-mutating': isMutating
|
||||||
<h4>Fetching...</h4>
|
});
|
||||||
) : (canEditInfo ? (
|
return (
|
||||||
<label>
|
<React.Fragment>
|
||||||
<textarea
|
<textarea
|
||||||
rows="5"
|
rows="20"
|
||||||
cols="100"
|
className={fieldClassName}
|
||||||
disabled={isMutating}
|
disabled={isMutating || !canEditInfo || isFetching}
|
||||||
defaultValue={description}
|
defaultValue={description}
|
||||||
onBlur={e => e.target.value !== description &&
|
onBlur={e => e.target.value !== description &&
|
||||||
handleUpdate(e.target.value)}
|
handleUpdate(e.target.value)}
|
||||||
/>
|
/>
|
||||||
{descriptionError && <div>Error mutating description: {descriptionError}</div>}
|
{descriptionError && <div>Error mutating description: {descriptionError}</div>}
|
||||||
</label>
|
</React.Fragment>
|
||||||
) : (
|
|
||||||
<div>{description}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
StudioDescription.propTypes = {
|
StudioDescription.propTypes = {
|
||||||
descriptionError: PropTypes.string,
|
descriptionError: PropTypes.string,
|
||||||
|
|
|
@ -2,43 +2,42 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import {selectIsFollowing, selectIsFetchingRoles} from '../../redux/studio';
|
import {selectIsFollowing} from '../../redux/studio';
|
||||||
import {selectCanFollowStudio} from '../../redux/studio-permissions';
|
import {selectCanFollowStudio} from '../../redux/studio-permissions';
|
||||||
import {
|
import {
|
||||||
mutateFollowingStudio, selectIsMutatingFollowing, selectFollowingMutationError
|
mutateFollowingStudio, selectIsMutatingFollowing, selectFollowingMutationError
|
||||||
} from '../../redux/studio-mutations';
|
} from '../../redux/studio-mutations';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const StudioFollow = ({
|
const StudioFollow = ({
|
||||||
canFollow,
|
canFollow,
|
||||||
isFetching,
|
|
||||||
isFollowing,
|
isFollowing,
|
||||||
isMutating,
|
isMutating,
|
||||||
followingError,
|
followingError,
|
||||||
handleFollow
|
handleFollow
|
||||||
}) => (
|
}) => {
|
||||||
<div>
|
if (!canFollow) return null;
|
||||||
<h3>Following</h3>
|
const fieldClassName = classNames('button', {
|
||||||
<div>
|
'mod-mutating': isMutating
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
<button
|
<button
|
||||||
disabled={isFetching || isMutating || !canFollow}
|
className={fieldClassName}
|
||||||
|
disabled={isMutating}
|
||||||
onClick={() => handleFollow(!isFollowing)}
|
onClick={() => handleFollow(!isFollowing)}
|
||||||
>
|
>
|
||||||
{isFetching ? (
|
{isMutating ? '...' : (
|
||||||
'Fetching...'
|
isFollowing ? 'Unfollow Studio' : 'Follow Studio'
|
||||||
) : (
|
|
||||||
isFollowing ? 'Unfollow' : 'Follow'
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{followingError && <div>Error mutating following: {followingError}</div>}
|
{followingError && <div>Error mutating following: {followingError}</div>}
|
||||||
{!canFollow && <div>Must be logged in to follow</div>}
|
</React.Fragment >
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
StudioFollow.propTypes = {
|
StudioFollow.propTypes = {
|
||||||
canFollow: PropTypes.bool,
|
canFollow: PropTypes.bool,
|
||||||
isFetching: PropTypes.bool,
|
|
||||||
isFollowing: PropTypes.bool,
|
isFollowing: PropTypes.bool,
|
||||||
isMutating: PropTypes.bool,
|
isMutating: PropTypes.bool,
|
||||||
followingError: PropTypes.string,
|
followingError: PropTypes.string,
|
||||||
|
@ -48,7 +47,6 @@ StudioFollow.propTypes = {
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
canFollow: selectCanFollowStudio(state),
|
canFollow: selectCanFollowStudio(state),
|
||||||
isFetching: selectIsFetchingRoles(state),
|
|
||||||
isMutating: selectIsMutatingFollowing(state),
|
isMutating: selectIsMutatingFollowing(state),
|
||||||
isFollowing: selectIsFollowing(state),
|
isFollowing: selectIsFollowing(state),
|
||||||
followingError: selectFollowingMutationError(state)
|
followingError: selectFollowingMutationError(state)
|
||||||
|
|
|
@ -8,27 +8,25 @@ import {selectCanEditInfo} from '../../redux/studio-permissions';
|
||||||
import {
|
import {
|
||||||
mutateStudioImage, selectIsMutatingImage, selectImageMutationError
|
mutateStudioImage, selectIsMutatingImage, selectImageMutationError
|
||||||
} from '../../redux/studio-mutations';
|
} from '../../redux/studio-mutations';
|
||||||
import Spinner from '../../components/spinner/spinner.jsx';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const blankImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||||
const StudioImage = ({
|
const StudioImage = ({
|
||||||
imageError, isFetching, isMutating, image, canEditInfo, handleUpdate
|
imageError, isFetching, isMutating, image, canEditInfo, handleUpdate
|
||||||
}) => (
|
}) => {
|
||||||
<div>
|
const fieldClassName = classNames('studio-image', {
|
||||||
<h3>Image</h3>
|
'mod-fetching': isFetching,
|
||||||
{isFetching ? (
|
'mod-mutating': isMutating
|
||||||
<h4>Fetching...</h4>
|
});
|
||||||
) : (
|
const src = isMutating ? blankImage : (image || blankImage);
|
||||||
<div>
|
return (
|
||||||
<div style={{width: '200px', height: '150px', border: '1px solid green'}}>
|
<div className={fieldClassName}>
|
||||||
{isMutating ?
|
|
||||||
<Spinner color="blue" /> :
|
|
||||||
<img
|
<img
|
||||||
style={{objectFit: 'contain'}}
|
style={{width: '300px', height: '225px', objectFit: 'cover'}}
|
||||||
src={image}
|
src={src}
|
||||||
/>}
|
/>
|
||||||
</div>
|
{canEditInfo && !isFetching &&
|
||||||
{canEditInfo &&
|
<React.Fragment>
|
||||||
<label>
|
|
||||||
<input
|
<input
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
type="file"
|
type="file"
|
||||||
|
@ -39,12 +37,11 @@ const StudioImage = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{imageError && <div>Error mutating image: {imageError}</div>}
|
{imageError && <div>Error mutating image: {imageError}</div>}
|
||||||
</label>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
StudioImage.propTypes = {
|
StudioImage.propTypes = {
|
||||||
imageError: PropTypes.string,
|
imageError: PropTypes.string,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, {useEffect} from 'react';
|
import React, {useEffect} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import Debug from './debug.jsx';
|
|
||||||
import StudioDescription from './studio-description.jsx';
|
import StudioDescription from './studio-description.jsx';
|
||||||
import StudioFollow from './studio-follow.jsx';
|
import StudioFollow from './studio-follow.jsx';
|
||||||
import StudioTitle from './studio-title.jsx';
|
import StudioTitle from './studio-title.jsx';
|
||||||
|
@ -11,7 +10,7 @@ import {selectIsLoggedIn} from '../../redux/session';
|
||||||
import {getInfo, getRoles} from '../../redux/studio';
|
import {getInfo, getRoles} from '../../redux/studio';
|
||||||
|
|
||||||
const StudioInfo = ({
|
const StudioInfo = ({
|
||||||
isLoggedIn, studio, onLoadInfo, onLoadRoles
|
isLoggedIn, onLoadInfo, onLoadRoles
|
||||||
}) => {
|
}) => {
|
||||||
useEffect(() => { // Load studio info after first render
|
useEffect(() => { // Load studio info after first render
|
||||||
onLoadInfo();
|
onLoadInfo();
|
||||||
|
@ -22,30 +21,23 @@ const StudioInfo = ({
|
||||||
}, [isLoggedIn]);
|
}, [isLoggedIn]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<React.Fragment>
|
||||||
<h2>Studio Info</h2>
|
|
||||||
<StudioTitle />
|
<StudioTitle />
|
||||||
<StudioDescription />
|
|
||||||
<StudioFollow />
|
<StudioFollow />
|
||||||
<StudioImage />
|
<StudioImage />
|
||||||
<Debug
|
<StudioDescription />
|
||||||
label="Studio Info"
|
</React.Fragment>
|
||||||
data={studio}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
StudioInfo.propTypes = {
|
StudioInfo.propTypes = {
|
||||||
isLoggedIn: PropTypes.bool,
|
isLoggedIn: PropTypes.bool,
|
||||||
studio: PropTypes.shape({}), // TODO remove, just for <Debug />
|
|
||||||
onLoadInfo: PropTypes.func,
|
onLoadInfo: PropTypes.func,
|
||||||
onLoadRoles: PropTypes.func
|
onLoadRoles: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
studio: state.studio,
|
|
||||||
isLoggedIn: selectIsLoggedIn(state)
|
isLoggedIn: selectIsLoggedIn(state)
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useRouteMatch, NavLink} from 'react-router-dom';
|
import {useRouteMatch, NavLink} from 'react-router-dom';
|
||||||
|
import SubNavigation from '../../components/subnavigation/subnavigation.jsx';
|
||||||
|
|
||||||
const StudioTabNav = () => {
|
const StudioTabNav = () => {
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<SubNavigation
|
||||||
|
align="left"
|
||||||
|
className="studio-tab-nav"
|
||||||
|
>
|
||||||
<NavLink
|
<NavLink
|
||||||
activeStyle={{textDecoration: 'underline'}}
|
activeClassName="active"
|
||||||
to={`${match.url}`}
|
to={`${match.url}`}
|
||||||
exact
|
exact
|
||||||
>
|
>
|
||||||
Projects
|
<li>Projects</li>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
|
||||||
<NavLink
|
<NavLink
|
||||||
activeStyle={{textDecoration: 'underline'}}
|
activeClassName="active"
|
||||||
to={`${match.url}/curators`}
|
to={`${match.url}/curators`}
|
||||||
>
|
>
|
||||||
Curators
|
<li>Curators</li>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
|
||||||
<NavLink
|
<NavLink
|
||||||
activeStyle={{textDecoration: 'underline'}}
|
activeClassName="active"
|
||||||
to={`${match.url}/comments`}
|
to={`${match.url}/comments`}
|
||||||
>
|
>
|
||||||
Comments
|
<li> Comments</li>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
|
||||||
<NavLink
|
<NavLink
|
||||||
activeStyle={{textDecoration: 'underline'}}
|
activeClassName="active"
|
||||||
to={`${match.url}/activity`}
|
to={`${match.url}/activity`}
|
||||||
>
|
>
|
||||||
Activity
|
<li>Activity</li>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</SubNavigation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,29 +6,28 @@ import {connect} from 'react-redux';
|
||||||
import {selectStudioTitle, selectIsFetchingInfo} from '../../redux/studio';
|
import {selectStudioTitle, selectIsFetchingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo} from '../../redux/studio-permissions';
|
import {selectCanEditInfo} from '../../redux/studio-permissions';
|
||||||
import {mutateStudioTitle, selectIsMutatingTitle, selectTitleMutationError} from '../../redux/studio-mutations';
|
import {mutateStudioTitle, selectIsMutatingTitle, selectTitleMutationError} from '../../redux/studio-mutations';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const StudioTitle = ({
|
const StudioTitle = ({
|
||||||
titleError, isFetching, isMutating, title, canEditInfo, handleUpdate
|
titleError, isFetching, isMutating, title, canEditInfo, handleUpdate
|
||||||
}) => (
|
}) => {
|
||||||
<div>
|
const fieldClassName = classNames('studio-title', {
|
||||||
<h3>Title</h3>
|
'mod-fetching': isFetching,
|
||||||
{isFetching ? (
|
'mod-mutating': isMutating
|
||||||
<h4>Fetching...</h4>
|
});
|
||||||
) : (canEditInfo ? (
|
return (
|
||||||
<label>
|
<React.Fragment>
|
||||||
<input
|
<textarea
|
||||||
disabled={isMutating}
|
className={fieldClassName}
|
||||||
|
disabled={isMutating || !canEditInfo || isFetching}
|
||||||
defaultValue={title}
|
defaultValue={title}
|
||||||
onBlur={e => e.target.value !== title &&
|
onBlur={e => e.target.value !== title &&
|
||||||
handleUpdate(e.target.value)}
|
handleUpdate(e.target.value)}
|
||||||
/>
|
/>
|
||||||
{titleError && <div>Error mutating title: {titleError}</div>}
|
{titleError && <div>Error mutating title: {titleError}</div>}
|
||||||
</label>
|
</React.Fragment>
|
||||||
) : (
|
|
||||||
<div>{title}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
StudioTitle.propTypes = {
|
StudioTitle.propTypes = {
|
||||||
titleError: PropTypes.string,
|
titleError: PropTypes.string,
|
||||||
|
|
|
@ -28,13 +28,17 @@ const {getInitialState, studioReducer} = require('../../redux/studio');
|
||||||
const {commentsReducer} = require('../../redux/comments');
|
const {commentsReducer} = require('../../redux/comments');
|
||||||
const {studioMutationsReducer} = require('../../redux/studio-mutations');
|
const {studioMutationsReducer} = require('../../redux/studio-mutations');
|
||||||
|
|
||||||
|
import './studio.scss';
|
||||||
|
|
||||||
const StudioShell = () => {
|
const StudioShell = () => {
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{maxWidth: '960px', margin: 'auto'}}>
|
<div className="studio-shell">
|
||||||
|
<div className="studio-info">
|
||||||
<StudioInfo />
|
<StudioInfo />
|
||||||
<hr />
|
</div>
|
||||||
|
<div className="studio-tabs">
|
||||||
<StudioTabNav />
|
<StudioTabNav />
|
||||||
<div>
|
<div>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -57,11 +61,12 @@ const StudioShell = () => {
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<Page>
|
<Page className="studio-page">
|
||||||
<Router>
|
<Router>
|
||||||
<Switch>
|
<Switch>
|
||||||
{/* Use variable studioPath to support /studio-playground/ or future route */}
|
{/* Use variable studioPath to support /studio-playground/ or future route */}
|
||||||
|
|
98
src/views/studio/studio.scss
Normal file
98
src/views/studio/studio.scss
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
@import "../../colors";
|
||||||
|
@import "../../frameless";
|
||||||
|
|
||||||
|
$radius: 8px;
|
||||||
|
|
||||||
|
.studio-page {
|
||||||
|
background-color: #E9F1FC;
|
||||||
|
|
||||||
|
#view {
|
||||||
|
/* Reset some defaults on width and margin */
|
||||||
|
background-color: transparent;
|
||||||
|
max-width: 1240px;
|
||||||
|
min-width: auto;
|
||||||
|
margin: 50px auto;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.studio-shell {
|
||||||
|
padding: 0 20px;
|
||||||
|
display: grid;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
/* Side-by-side with fixed width sidebar */
|
||||||
|
grid-template-columns: 300px minmax(0, 1fr);
|
||||||
|
|
||||||
|
/* Stack vertically at medium size and smaller */
|
||||||
|
@media #{$medium-and-smaller} {
|
||||||
|
& {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.studio-info {
|
||||||
|
justify-self: center;
|
||||||
|
width: 300px;
|
||||||
|
height: fit-content;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.studio-title, .studio-description {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0 -8px; /* Outset the border horizontally */
|
||||||
|
padding: 5px 8px;
|
||||||
|
border: 2px dashed $ui-blue-25percent;
|
||||||
|
border-radius: $radius;
|
||||||
|
resize: none;
|
||||||
|
&:disabled { border-color: transparent; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.studio-title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.studio-description:disabled {
|
||||||
|
background: $ui-blue-10percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.studio-tab-nav {
|
||||||
|
border-bottom: 1px solid $active-dark-gray;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
li { background: $active-gray; }
|
||||||
|
.active > li { background: $ui-blue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Modification classes for different interaction states */
|
||||||
|
.mod-fetching { /* When a field has no content to display yet */
|
||||||
|
position: relative;
|
||||||
|
min-height: 30px;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background: #a0c6fc;
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
/* For elements that can't use :after, force reset some internals
|
||||||
|
to get the same visual (e.g. for textareas)*/
|
||||||
|
border-radius: $radius;
|
||||||
|
background: #a0c6fc !important;
|
||||||
|
color: #a0c6fc !important;
|
||||||
|
border: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-mutating { /* When a field has sent a change to the server */
|
||||||
|
cursor: wait;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue