Skip to content

Drawing page continuation #7367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions app/css/bootcamp/components/solve-exercise-page.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,63 @@
.btn-primary {
@apply bg-bootcamp-purple;
}
.autorun-button {
@apply btn-primary btn-s;
@apply flex items-stretch;
@apply p-0;

box-shadow: 0px 3px 6px rgba(var(--shadowColorMain), 0.4);

.primary-segment,
.autorun-segment {
transition: background 0.1s ease-in;
}
&:not(:disabled):not(.--disabled):hover {
@apply bg-purple !important;
}

.primary-segment {
@apply flex items-center;
@apply font-semibold;
@apply px-16;
@apply border-r-1 border-borderColor3;

&.disabled {
@apply bg-purpleDarkened;
opacity: 0.5;
cursor: not-allowed;
}

&:not(.disabled) {
&:hover {
@apply !bg-purpleDarkened;
}
}
}
.autorun-segment {
@apply grid place-items-center px-12;
@apply rounded-r-3;
& .c-icon {
height: 18px;
width: 18px;
}

&:hover {
@apply bg-purpleDarkened;
}
&.on {
@apply bg-purpleDarkened;
& .c-icon {
@apply filter-lightGold;
}
}
&.off {
& .c-icon {
@apply filter-white;
}
}
}
}
}

body.namespace-bootcamp.controller-exercises.action-edit {
Expand Down
1 change: 1 addition & 0 deletions app/images/bootcamp/autorun.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/images/bootcamp/edit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 90 additions & 30 deletions app/javascript/components/bootcamp/DrawingPage/DrawingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react'
import React, { useCallback, useMemo, useState } from 'react'
import { Header, StudentCodeGetter } from './Header/Header'
import {
Resizer,
Expand All @@ -11,6 +11,11 @@ import { useLocalStorage } from '@uidotdev/usehooks'
import Scrubber from '../SolveExercisePage/Scrubber/Scrubber'
import { debounce } from 'lodash'
import { useSetupDrawingPage } from './useSetupDrawingPage'
import SolveExercisePageContextWrapper from '../SolveExercisePage/SolveExercisePageContextWrapper'
import useEditorStore from '../SolveExercisePage/store/editorStore'
import { assembleClassNames } from '@/utils/assemble-classnames'
import { Icon } from '@/components/common'
import { StaticTooltip } from '../SolveExercisePage/Scrubber/ScrubberTooltipInformation'

export default function DrawingPage({
drawing,
Expand All @@ -27,9 +32,8 @@ export default function DrawingPage({
} = useResizablePanels({
initialSize: 800,
direction: 'horizontal',
localStorageId: 'drawing-page-lhs',
localStorageId: 'solve-exercise-page-lhs',
})

const {
handleRunCode,
handleEditorDidMount,
Expand Down Expand Up @@ -61,36 +65,92 @@ export default function DrawingPage({
}, 5000)
}, [setEditorLocalStorageValue])

const { shouldAutoRunCode, toggleShouldAutoRunCode } = useEditorStore()
const handleToggleAutoRun = useCallback(
(shouldAutoRunCode: boolean) => {
if (!shouldAutoRunCode) {
handleRunCode()
}
toggleShouldAutoRunCode()
},
[shouldAutoRunCode]
)

return (
<div id="bootcamp-solve-exercise-page">
<Header
links={links}
backgrounds={backgrounds}
savingStateLabel={savingStateLabel}
drawing={drawing}
setBackgroundImage={setBackgroundImage}
/>
<div className="page-body">
<div style={{ width: LHSWidth }} className="page-body-lhs">
<ErrorBoundary>
<CodeMirror
style={{ height: `100%` }}
ref={editorViewRef}
editorDidMount={handleEditorDidMount}
handleRunCode={handleRunCode}
setEditorLocalStorageValue={setEditorLocalStorageValue}
onEditorChangeCallback={patchCodeOnDebounce}
/>
<Scrubber animationTimeline={animationTimeline} frames={frames} />
</ErrorBoundary>
</div>
<Resizer direction="vertical" handleMouseDown={handleMouseDown} />
{/* RHS */}
<div className="page-body-rhs" style={{ width: RHSWidth }}>
<div ref={viewContainerRef} id="view-container" />
<SolveExercisePageContextWrapper
value={{
editorView: editorViewRef.current,
}}
>
<div id="bootcamp-solve-exercise-page">
<Header
links={links}
backgrounds={backgrounds}
savingStateLabel={savingStateLabel}
drawing={drawing}
setBackgroundImage={setBackgroundImage}
/>
<div className="page-body">
<div style={{ width: LHSWidth }} className="page-body-lhs">
<ErrorBoundary>
<CodeMirror
style={{ height: `100%` }}
ref={editorViewRef}
editorDidMount={handleEditorDidMount}
handleRunCode={handleRunCode}
setEditorLocalStorageValue={setEditorLocalStorageValue}
onEditorChangeCallback={patchCodeOnDebounce}
/>

<div className="flex items-center w-full">
<div className="autorun-button">
<button
onClick={handleRunCode}
className={assembleClassNames(
'primary-segment',
shouldAutoRunCode ? 'disabled' : ''
)}
>
Run Code
</button>
<button
className={assembleClassNames(
'autorun-segment',
shouldAutoRunCode ? 'on' : 'off',
'relative group'
)}
onClick={() => handleToggleAutoRun(shouldAutoRunCode)}
>
<Icon
alt="Autorun"
icon="autorun"
category="bootcamp"
className="w-[20px] h-[20px]"
/>
<StaticTooltip
text={
shouldAutoRunCode
? 'Autorun is on. Click to turn off.'
: 'Autorun is off. Click to turn on.'
}
/>
</button>
</div>
<Scrubber
animationTimeline={animationTimeline}
frames={frames}
/>
</div>
</ErrorBoundary>
</div>
<Resizer direction="vertical" handleMouseDown={handleMouseDown} />
{/* RHS */}
<div className="page-body-rhs" style={{ width: RHSWidth }}>
<div ref={viewContainerRef} id="view-container" />
</div>
</div>
</div>
</div>
</SolveExercisePageContextWrapper>
)
}

Expand Down
59 changes: 43 additions & 16 deletions app/javascript/components/bootcamp/DrawingPage/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ function _Header({
setBackgroundImage: ((imageUrl: string | null) => void) | null
} & Pick<DrawingPageProps, 'links' | 'drawing' | 'backgrounds'>) {
const [titleInputValue, setTitleInputValue] = useState(drawing.title)
const [prevTitleInputValue, setPrevTitleInputValue] = useState(drawing.title)
const [editMode, setEditMode] = useState(false)
const [titleSavingStateLabel, setTitleSavingStateLabel] = useState<string>(
DEFAULT_SAVE_BUTTON_LABEL
)
const inputRef = React.useRef<HTMLInputElement>(null)

const handleSaveTitle = useCallback(() => {
setTitleSavingStateLabel('Saving...')
Expand All @@ -43,6 +45,22 @@ function _Header({
[setBackgroundImage]
)

const handleSwitchToEditMode = useCallback(() => {
setEditMode(true)
setPrevTitleInputValue(titleInputValue)
}, [titleInputValue])

const handleCancelEditMode = useCallback(() => {
setTitleInputValue(prevTitleInputValue)
setEditMode(false)
}, [prevTitleInputValue])

useEffect(() => {
if (editMode) {
inputRef.current?.focus()
}
}, [editMode])

// setup the background on mount
useEffect(() => {
if (setBackgroundImage && drawing.backgroundSlug) {
Expand Down Expand Up @@ -77,7 +95,8 @@ function _Header({
)
handleBackgroundChange(selectedBackground)
}}
value={drawing.backgroundSlug}
className="bg-backgroundColorD rounded-5 py-4 px-8 font-medium"
defaultValue={drawing.backgroundSlug}
>
{backgrounds.map((background) => (
<option
Expand All @@ -89,35 +108,43 @@ function _Header({
</option>
))}
</select>
<div className="flex items-center gap-12">
<div className="flex items-center gap-4">
{editMode ? (
<>
<button onClick={handleSaveTitle} className="btn-primary btn-xxs">
{titleSavingStateLabel}
</button>
<button
className="btn-secondary btn-xxs"
onClick={() => setEditMode(false)}
>
Cancel
</button>
<input
value={titleInputValue}
ref={inputRef}
onChange={(e) => {
setTitleInputValue(e.target.value)
setTitleSavingStateLabel(DEFAULT_SAVE_BUTTON_LABEL)
}}
type="text"
style={{ all: 'unset', borderBottom: '1px solid' }}
className="!py-2 !text-14 !rounded-3 !border-borderColor5"
/>
<button onClick={handleSaveTitle} className="btn-primary btn-xxs">
{titleSavingStateLabel}
</button>
<button
className="btn-secondary btn-xxs"
onClick={handleCancelEditMode}
>
Cancel
</button>
</>
) : (
<>
<div className="bg-backgroundColorD rounded-5 py-6 px-12">
<span className="font-medium mr-12">
Title: {titleInputValue}
</span>
<button onClick={() => setEditMode(true)}>
<GraphicalIcon icon="edit" height={15} width={15} />
<GraphicalIcon
icon="edit"
category="bootcamp"
height={15}
width={15}
/>
</button>
<span>{titleInputValue}</span>
</>
</div>
)}
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ export function useSetupDrawingPage({
setEditorLocalStorageValue,
code,
}) {
const { setDefaultCode, setShouldAutoRunCode } = useEditorStore()
const { setDefaultCode, setActiveEditor, setShouldAutoRunCode } =
useEditorStore()

// Setup hook
useEffect(() => {
setActiveEditor('drawing')
const storedShouldAutoRunCode = localStorage.getItem(
'drawing-should-autorun-code'
)
setShouldAutoRunCode(storedShouldAutoRunCode === 'true')
if (
editorLocalStorageValue.storedAt &&
code.storedAt &&
Expand All @@ -22,6 +28,5 @@ export function useSetupDrawingPage({
// otherwise we are using the code from the storage
setDefaultCode(editorLocalStorageValue.code)
}
setShouldAutoRunCode(true)
}, [code, setDefaultCode, setEditorLocalStorageValue])
}, [])
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ export const CodeMirror = forwardRef(function _CodeMirror(
}, 500)
}, [setEditorLocalStorageValue, readOnlyRangesStateField])

const autoRunOnDebounce = useMemo(() => {
return debounce(() => {
handleRunCode()
}, 500)
}, [])

let value = defaultCode

const getEditorView = (): EditorView | null => {
Expand Down Expand Up @@ -207,7 +213,7 @@ export const CodeMirror = forwardRef(function _CodeMirror(
() => {
const { shouldAutoRunCode } = useEditorStore.getState()
if (shouldAutoRunCode) {
handleRunCode()
autoRunOnDebounce()
}
},
() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@ export class InformationWidget extends WidgetType {

private createRefElement() {
const refElement = document.createElement('span')
refElement.classList.add('font-bold', 'text-black')
// refElement.style.float = 'right'
refElement.style.position = 'absolute'
refElement.style.right = '0'
refElement.innerText = ' '

return refElement
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function Scrubber({
rangeRef.current?.focus()
}}
tabIndex={-1}
className="relative group"
className="relative group w-full"
>
{animationTimeline && (
<PlayButton
Expand Down
Loading
Loading