Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
scratch-www
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
scratch-www
Commits
4f54e14e
Unverified
Commit
4f54e14e
authored
May 03, 2021
by
Paul Kaplan
Committed by
GitHub
May 03, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5306 from paulkaplan/studio-info-styling
Studio info styling
parents
4f29d89b
8c104ed3
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
236 additions
and
149 deletions
+236
-149
src/redux/studio.js
src/redux/studio.js
+5
-6
src/views/studio/studio-description.jsx
src/views/studio/studio-description.jsx
+20
-22
src/views/studio/studio-follow.jsx
src/views/studio/studio-follow.jsx
+16
-18
src/views/studio/studio-image.jsx
src/views/studio/studio-image.jsx
+31
-34
src/views/studio/studio-info.jsx
src/views/studio/studio-info.jsx
+4
-12
src/views/studio/studio-tab-nav.jsx
src/views/studio/studio-tab-nav.jsx
+14
-13
src/views/studio/studio-title.jsx
src/views/studio/studio-title.jsx
+19
-20
src/views/studio/studio.jsx
src/views/studio/studio.jsx
+29
-24
src/views/studio/studio.scss
src/views/studio/studio.scss
+98
-0
No files found.
src/redux/studio.js
View file @
4f54e14e
...
...
@@ -13,7 +13,7 @@ const Status = keyMirror({
});
const
getInitialState
=
()
=>
({
infoStatus
:
Status
.
NOT_FETCHED
,
infoStatus
:
Status
.
FETCHING
,
title
:
''
,
description
:
''
,
openToAll
:
false
,
...
...
@@ -38,12 +38,14 @@ const studioReducer = (state, action) => {
case
'
SET_INFO
'
:
return
{
...
state
,
...
action
.
info
...
action
.
info
,
infoStatus
:
Status
.
FETCHED
};
case
'
SET_ROLES
'
:
return
{
...
state
,
...
action
.
roles
...
action
.
roles
,
rolesStatus
:
Status
.
FETCHED
};
case
'
SET_FETCH_STATUS
'
:
if
(
action
.
error
)
{
...
...
@@ -95,14 +97,12 @@ const selectIsFetchingRoles = state => state.studio.rolesStatus === Status.FETCH
// Thunks
const
getInfo
=
()
=>
((
dispatch
,
getState
)
=>
{
dispatch
(
setFetchStatus
(
'
infoStatus
'
,
Status
.
FETCHING
));
const
studioId
=
selectStudioId
(
getState
());
api
({
uri
:
`/studios/
${
studioId
}
`
},
(
err
,
body
,
res
)
=>
{
if
(
err
||
typeof
body
===
'
undefined
'
||
res
.
statusCode
!==
200
)
{
dispatch
(
setFetchStatus
(
'
infoStatus
'
,
Status
.
ERROR
,
err
));
return
;
}
dispatch
(
setFetchStatus
(
'
infoStatus
'
,
Status
.
FETCHED
));
dispatch
(
setInfo
({
title
:
body
.
title
,
description
:
body
.
description
,
...
...
@@ -130,7 +130,6 @@ const getRoles = () => ((dispatch, getState) => {
dispatch
(
setFetchStatus
(
'
rolesStatus
'
,
Status
.
ERROR
,
err
));
return
;
}
dispatch
(
setFetchStatus
(
'
rolesStatus
'
,
Status
.
FETCHED
));
dispatch
(
setRoles
({
manager
:
body
.
manager
,
curator
:
body
.
curator
,
...
...
src/views/studio/studio-description.jsx
View file @
4f54e14e
...
...
@@ -8,31 +8,29 @@ import {selectCanEditInfo} from '../../redux/studio-permissions';
import
{
mutateStudioDescription
,
selectIsMutatingDescription
,
selectDescriptionMutationError
}
from
'
../../redux/studio-mutations
'
;
import
classNames
from
'
classnames
'
;
const
StudioDescription
=
({
descriptionError
,
isFetching
,
isMutating
,
description
,
canEditInfo
,
handleUpdate
})
=>
(
<
div
>
<
h3
>
Description
</
h3
>
{
isFetching
?
(
<
h4
>
Fetching...
</
h4
>
)
:
(
canEditInfo
?
(
<
label
>
})
=>
{
const
fieldClassName
=
classNames
(
'
studio-description
'
,
{
'
mod-fetching
'
:
isFetching
,
'
mod-mutating
'
:
isMutating
});
return
(
<
React
.
Fragment
>
<
textarea
rows=
"5
"
cols=
"100"
disabled=
{
isMutat
ing
}
rows=
"20
"
className=
{
fieldClassName
}
disabled=
{
isMutating
||
!
canEditInfo
||
isFetch
ing
}
defaultValue=
{
description
}
onBlur=
{
e
=>
e
.
target
.
value
!==
description
&&
handleUpdate
(
e
.
target
.
value
)
}
/>
{
descriptionError
&&
<
div
>
Error mutating description:
{
descriptionError
}
</
div
>
}
</
label
>
)
:
(
<
div
>
{
description
}
</
div
>
))
}
</
div
>
);
</
React
.
Fragment
>
);
};
StudioDescription
.
propTypes
=
{
descriptionError
:
PropTypes
.
string
,
...
...
src/views/studio/studio-follow.jsx
View file @
4f54e14e
...
...
@@ -2,43 +2,42 @@
import
React
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
connect
}
from
'
react-redux
'
;
import
{
selectIsFollowing
,
selectIsFetchingRoles
}
from
'
../../redux/studio
'
;
import
{
selectIsFollowing
}
from
'
../../redux/studio
'
;
import
{
selectCanFollowStudio
}
from
'
../../redux/studio-permissions
'
;
import
{
mutateFollowingStudio
,
selectIsMutatingFollowing
,
selectFollowingMutationError
}
from
'
../../redux/studio-mutations
'
;
import
classNames
from
'
classnames
'
;
const
StudioFollow
=
({
canFollow
,
isFetching
,
isFollowing
,
isMutating
,
followingError
,
handleFollow
})
=>
(
<
div
>
<
h3
>
Following
</
h3
>
<
div
>
})
=>
{
if
(
!
canFollow
)
return
null
;
const
fieldClassName
=
classNames
(
'
button
'
,
{
'
mod-mutating
'
:
isMutating
});
return
(
<
React
.
Fragment
>
<
button
disabled=
{
isFetching
||
isMutating
||
!
canFollow
}
className=
{
fieldClassName
}
disabled=
{
isMutating
}
onClick=
{
()
=>
handleFollow
(
!
isFollowing
)
}
>
{
isFetching
?
(
'
Fetching...
'
)
:
(
isFollowing
?
'
Unfollow
'
:
'
Follow
'
{
isMutating
?
'
...
'
:
(
isFollowing
?
'
Unfollow Studio
'
:
'
Follow Studio
'
)
}
</
button
>
{
followingError
&&
<
div
>
Error mutating following:
{
followingError
}
</
div
>
}
{
!
canFollow
&&
<
div
>
Must be logged in to follow
</
div
>
}
</
div
>
</
div
>
);
</
React
.
Fragment
>
);
};
StudioFollow
.
propTypes
=
{
canFollow
:
PropTypes
.
bool
,
isFetching
:
PropTypes
.
bool
,
isFollowing
:
PropTypes
.
bool
,
isMutating
:
PropTypes
.
bool
,
followingError
:
PropTypes
.
string
,
...
...
@@ -48,7 +47,6 @@ StudioFollow.propTypes = {
export
default
connect
(
state
=>
({
canFollow
:
selectCanFollowStudio
(
state
),
isFetching
:
selectIsFetchingRoles
(
state
),
isMutating
:
selectIsMutatingFollowing
(
state
),
isFollowing
:
selectIsFollowing
(
state
),
followingError
:
selectFollowingMutationError
(
state
)
...
...
src/views/studio/studio-image.jsx
View file @
4f54e14e
...
...
@@ -8,27 +8,25 @@ import {selectCanEditInfo} from '../../redux/studio-permissions';
import
{
mutateStudioImage
,
selectIsMutatingImage
,
selectImageMutationError
}
from
'
../../redux/studio-mutations
'
;
import
Spinner
from
'
../../components/spinner/spinner.jsx
'
;
import
classNames
from
'
classnames
'
;
const
blankImage
=
'
data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
'
;
const
StudioImage
=
({
imageError
,
isFetching
,
isMutating
,
image
,
canEditInfo
,
handleUpdate
})
=>
(
<
div
>
<
h3
>
Image
</
h3
>
{
isFetching
?
(
<
h4
>
Fetching...
</
h4
>
)
:
(
<
div
>
<
div
style=
{
{
width
:
'
200px
'
,
height
:
'
150px
'
,
border
:
'
1px solid green
'
}
}
>
{
isMutating
?
<
Spinner
color=
"blue"
/>
:
})
=>
{
const
fieldClassName
=
classNames
(
'
studio-image
'
,
{
'
mod-fetching
'
:
isFetching
,
'
mod-mutating
'
:
isMutating
});
const
src
=
isMutating
?
blankImage
:
(
image
||
blankImage
);
return
(
<
div
className=
{
fieldClassName
}
>
<
img
style=
{
{
objectFit
:
'
contain
'
}
}
src=
{
image
}
/>
}
</
div
>
{
canEditInfo
&&
<
label
>
style=
{
{
width
:
'
300px
'
,
height
:
'
225px
'
,
objectFit
:
'
cover
'
}
}
src=
{
src
}
/>
{
canEditInfo
&&
!
isFetching
&&
<
React
.
Fragment
>
<
input
disabled=
{
isMutating
}
type=
"file"
...
...
@@ -39,12 +37,11 @@ const StudioImage = ({
}
}
/>
{
imageError
&&
<
div
>
Error mutating image:
{
imageError
}
</
div
>
}
</
label
>
</
React
.
Fragment
>
}
</
div
>
)
}
</
div
>
);
);
};
StudioImage
.
propTypes
=
{
imageError
:
PropTypes
.
string
,
...
...
src/views/studio/studio-info.jsx
View file @
4f54e14e
import
React
,
{
useEffect
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
connect
}
from
'
react-redux
'
;
import
Debug
from
'
./debug.jsx
'
;
import
StudioDescription
from
'
./studio-description.jsx
'
;
import
StudioFollow
from
'
./studio-follow.jsx
'
;
import
StudioTitle
from
'
./studio-title.jsx
'
;
...
...
@@ -11,7 +10,7 @@ import {selectIsLoggedIn} from '../../redux/session';
import
{
getInfo
,
getRoles
}
from
'
../../redux/studio
'
;
const
StudioInfo
=
({
isLoggedIn
,
studio
,
onLoadInfo
,
onLoadRoles
isLoggedIn
,
onLoadInfo
,
onLoadRoles
})
=>
{
useEffect
(()
=>
{
// Load studio info after first render
onLoadInfo
();
...
...
@@ -22,30 +21,23 @@ const StudioInfo = ({
},
[
isLoggedIn
]);
return
(
<
div
>
<
h2
>
Studio Info
</
h2
>
<
React
.
Fragment
>
<
StudioTitle
/>
<
StudioDescription
/>
<
StudioFollow
/>
<
StudioImage
/>
<
Debug
label=
"Studio Info"
data=
{
studio
}
/>
</
div
>
<
StudioDescription
/>
</
React
.
Fragment
>
);
};
StudioInfo
.
propTypes
=
{
isLoggedIn
:
PropTypes
.
bool
,
studio
:
PropTypes
.
shape
({}),
// TODO remove, just for <Debug />
onLoadInfo
:
PropTypes
.
func
,
onLoadRoles
:
PropTypes
.
func
};
export
default
connect
(
state
=>
({
studio
:
state
.
studio
,
isLoggedIn
:
selectIsLoggedIn
(
state
)
}),
{
...
...
src/views/studio/studio-tab-nav.jsx
View file @
4f54e14e
import
React
from
'
react
'
;
import
{
useRouteMatch
,
NavLink
}
from
'
react-router-dom
'
;
import
SubNavigation
from
'
../../components/subnavigation/subnavigation.jsx
'
;
const
StudioTabNav
=
()
=>
{
const
match
=
useRouteMatch
();
return
(
<
div
>
<
SubNavigation
align=
"left"
className=
"studio-tab-nav"
>
<
NavLink
active
Style=
{
{
textDecoration
:
'
underline
'
}
}
active
ClassName=
"active"
to=
{
`${match.url}`
}
exact
>
Projects
<
li
>
Projects
</
li
>
</
NavLink
>
|
<
NavLink
active
Style=
{
{
textDecoration
:
'
underline
'
}
}
active
ClassName=
"active"
to=
{
`${match.url}/curators`
}
>
Curators
<
li
>
Curators
</
li
>
</
NavLink
>
|
<
NavLink
active
Style=
{
{
textDecoration
:
'
underline
'
}
}
active
ClassName=
"active"
to=
{
`${match.url}/comments`
}
>
Comments
<
li
>
Comments
</
li
>
</
NavLink
>
|
<
NavLink
active
Style=
{
{
textDecoration
:
'
underline
'
}
}
active
ClassName=
"active"
to=
{
`${match.url}/activity`
}
>
Activity
<
li
>
Activity
</
li
>
</
NavLink
>
</
div
>
</
SubNavigation
>
);
};
...
...
src/views/studio/studio-title.jsx
View file @
4f54e14e
...
...
@@ -6,29 +6,28 @@ import {connect} from 'react-redux';
import
{
selectStudioTitle
,
selectIsFetchingInfo
}
from
'
../../redux/studio
'
;
import
{
selectCanEditInfo
}
from
'
../../redux/studio-permissions
'
;
import
{
mutateStudioTitle
,
selectIsMutatingTitle
,
selectTitleMutationError
}
from
'
../../redux/studio-mutations
'
;
import
classNames
from
'
classnames
'
;
const
StudioTitle
=
({
titleError
,
isFetching
,
isMutating
,
title
,
canEditInfo
,
handleUpdate
})
=>
(
<
div
>
<
h3
>
Title
</
h3
>
{
isFetching
?
(
<
h4
>
Fetching...
</
h4
>
)
:
(
canEditInfo
?
(
<
label
>
<
input
disabled=
{
isMutating
}
})
=>
{
const
fieldClassName
=
classNames
(
'
studio-title
'
,
{
'
mod-fetching
'
:
isFetching
,
'
mod-mutating
'
:
isMutating
});
return
(
<
React
.
Fragment
>
<
textarea
className=
{
fieldClassName
}
disabled=
{
isMutating
||
!
canEditInfo
||
isFetching
}
defaultValue=
{
title
}
onBlur=
{
e
=>
e
.
target
.
value
!==
title
&&
handleUpdate
(
e
.
target
.
value
)
}
/>
{
titleError
&&
<
div
>
Error mutating title:
{
titleError
}
</
div
>
}
</
label
>
)
:
(
<
div
>
{
title
}
</
div
>
))
}
</
div
>
);
</
React
.
Fragment
>
);
};
StudioTitle
.
propTypes
=
{
titleError
:
PropTypes
.
string
,
...
...
src/views/studio/studio.jsx
View file @
4f54e14e
...
...
@@ -28,13 +28,17 @@ const {getInitialState, studioReducer} = require('../../redux/studio');
const
{
commentsReducer
}
=
require
(
'
../../redux/comments
'
);
const
{
studioMutationsReducer
}
=
require
(
'
../../redux/studio-mutations
'
);
import
'
./studio.scss
'
;
const
StudioShell
=
()
=>
{
const
match
=
useRouteMatch
();
return
(
<
div
style=
{
{
maxWidth
:
'
960px
'
,
margin
:
'
auto
'
}
}
>
<
div
className=
"studio-shell"
>
<
div
className=
"studio-info"
>
<
StudioInfo
/>
<
hr
/>
</
div
>
<
div
className=
"studio-tabs"
>
<
StudioTabNav
/>
<
div
>
<
Switch
>
...
...
@@ -57,11 +61,12 @@ const StudioShell = () => {
</
Switch
>
</
div
>
</
div
>
</
div
>
);
};
render
(
<
Page
>
<
Page
className=
"studio-page"
>
<
Router
>
<
Switch
>
{
/* Use variable studioPath to support /studio-playground/ or future route */
}
...
...
src/views/studio/studio.scss
0 → 100644
View file @
4f54e14e
@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
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment