diff --git a/package.json b/package.json index a0113308b8..7043155e51 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,11 @@ "ag-grid-react": "^32.3.1", "array-move": "^4.0.0", "browserfs": "^1.4.3", - "classnames": "^2.3.2", + "clsx": "^2.1.1", "conductor": "https://github.com/source-academy/conductor.git#0.2.1", "dayjs": "^1.11.13", "dompurify": "^3.2.4", + "es-toolkit": "^1.39.9", "flexboxgrid": "^6.3.1", "flexboxgrid-helpers": "^1.1.3", "hastscript": "^9.0.0", @@ -68,7 +69,6 @@ "js-yaml": "^4.1.0", "konva": "^9.2.0", "language-directory": "https://github.com/source-academy/language-directory.git", - "lodash": "^4.17.21", "lz-string": "^1.4.4", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-hast": "^13.0.0", diff --git a/src/commons/Markdown.tsx b/src/commons/Markdown.tsx index 0a15c40e9a..c59d7a0180 100644 --- a/src/commons/Markdown.tsx +++ b/src/commons/Markdown.tsx @@ -1,5 +1,5 @@ import { Classes } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import DOMPurify from 'dompurify'; import React from 'react'; import { Converter } from 'showdown'; @@ -24,7 +24,7 @@ const Markdown: React.FC = props => { return (
{ if (!isApiHealthy) { return ( -
+
( -
+
= ({ return (
-
+
= ({ src={overview.coverImage ? overview.coverImage : defaultCoverImage} />
-
+
= props => { if (!assessment?.questions.length) { return ( } /> @@ -1042,7 +1042,7 @@ It is safe to close this window.`} mobileSideContentProps: mobileSideContentProps(questionId) }; return ( -
+
{submissionOverlay} {overlay} {resetTemplateOverlay} diff --git a/src/commons/controlBar/ControlBar.tsx b/src/commons/controlBar/ControlBar.tsx index ad8eff695f..9b26f0f2e7 100644 --- a/src/commons/controlBar/ControlBar.tsx +++ b/src/commons/controlBar/ControlBar.tsx @@ -1,5 +1,5 @@ import { Classes } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; export type ControlBarProps = { @@ -10,17 +10,15 @@ export type ControlBarProps = { const ControlBar: React.FC = props => { const editorControl = ( -
- {props.editorButtons} -
+
{props.editorButtons}
); const flowControl = props.flowButtons && ( -
{props.flowButtons}
+
{props.flowButtons}
); const editingWorkspaceControl = ( -
+
{props.editingWorkspaceButtons}
); diff --git a/src/commons/dialogs/ConfirmDialog.tsx b/src/commons/dialogs/ConfirmDialog.tsx index 81f2dfbad9..281746988c 100644 --- a/src/commons/dialogs/ConfirmDialog.tsx +++ b/src/commons/dialogs/ConfirmDialog.tsx @@ -9,7 +9,7 @@ import { IconName, Intent } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import classes from 'src/styles/ConfirmDialog.module.scss'; @@ -34,7 +34,7 @@ export function ConfirmDialog( onClick={() => props.onResponse && props.onResponse(choice.key)} intent={choice.intent} fill={props.largeButtons} - className={classNames(props.largeButtons && classes['large-button'])} + className={clsx(props.largeButtons && classes['large-button'])} {...choice.props} > {choice.label} @@ -47,7 +47,7 @@ export function ConfirmDialog( : () => props.onResponse && props.onResponse(escapeResponse); return ( = props => { if (assessment === null || assessment!.questions.length === 0) { return ( } /> @@ -707,7 +707,7 @@ const EditingWorkspace: React.FC = props => { } }; return ( -
+
{resetTemplateOverlay()}
diff --git a/src/commons/editor/EditorContainer.tsx b/src/commons/editor/EditorContainer.tsx index 1f116885b7..29731899fc 100644 --- a/src/commons/editor/EditorContainer.tsx +++ b/src/commons/editor/EditorContainer.tsx @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { pick } from 'es-toolkit'; import React from 'react'; import SourcecastEditor, { @@ -40,7 +40,7 @@ export const convertEditorTabStateToProps = ( return { editorTabIndex, editorValue: editorTab.value, - ..._.pick(editorTab, 'filePath', 'highlightedLines', 'breakpoints', 'newCursorPosition') + ...pick(editorTab, ['filePath', 'highlightedLines', 'breakpoints', 'newCursorPosition']) }; }; diff --git a/src/commons/editor/tabs/EditorTab.tsx b/src/commons/editor/tabs/EditorTab.tsx index 9eabde9332..e8d0c97abb 100644 --- a/src/commons/editor/tabs/EditorTab.tsx +++ b/src/commons/editor/tabs/EditorTab.tsx @@ -1,6 +1,6 @@ import { Card, Icon } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; type Props = { @@ -19,7 +19,7 @@ const EditorTab: React.FC = ({ filePath, isActive, setActive, remove }) = return ( = ({
{children} toggleMenu(false)} diff --git a/src/commons/gitHubOverlay/FileExplorerDialog.tsx b/src/commons/gitHubOverlay/FileExplorerDialog.tsx index 6afffd2236..c611a605a8 100644 --- a/src/commons/gitHubOverlay/FileExplorerDialog.tsx +++ b/src/commons/gitHubOverlay/FileExplorerDialog.tsx @@ -12,7 +12,7 @@ import { } from '@blueprintjs/core'; import { Octokit } from '@octokit/rest'; import { GetResponseTypeFromEndpointMethod } from '@octokit/types'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useEffect, useState } from 'react'; import { @@ -46,7 +46,7 @@ const FileExplorerDialog: React.FC = props => { return ( -
+

Select a File

@@ -55,7 +55,7 @@ const FileExplorerDialog: React.FC = props => { onNodeClick={handleNodeClick} onNodeCollapse={handleNodeCollapse} onNodeExpand={handleNodeExpand} - className={classNames('FileTree', Classes.ELEVATION_0)} + className={clsx('FileTree', Classes.ELEVATION_0)} /> {props.pickerType === 'Save' && (
diff --git a/src/commons/gitHubOverlay/RepositoryDialog.tsx b/src/commons/gitHubOverlay/RepositoryDialog.tsx index 97e539c753..fad3616051 100644 --- a/src/commons/gitHubOverlay/RepositoryDialog.tsx +++ b/src/commons/gitHubOverlay/RepositoryDialog.tsx @@ -9,7 +9,7 @@ import { Radio, RadioGroup } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useState } from 'react'; import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; @@ -24,7 +24,7 @@ const RepositoryDialog: React.FC = props => { return ( -
+

Select a Repository

diff --git a/src/commons/grading/GradingText.tsx b/src/commons/grading/GradingText.tsx index a96d7bb75a..0b61f0e4b3 100644 --- a/src/commons/grading/GradingText.tsx +++ b/src/commons/grading/GradingText.tsx @@ -1,5 +1,5 @@ import { Classes, Text } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; const defaultStyles: React.CSSProperties = { @@ -17,7 +17,7 @@ type Props = { const GradingText: React.FC = ({ children, style, isSecondaryText, className }) => { return ( {children} diff --git a/src/commons/mobileWorkspace/mobileSideContent/MobileSideContent.tsx b/src/commons/mobileWorkspace/mobileSideContent/MobileSideContent.tsx index 08646a28c5..956bc9df96 100644 --- a/src/commons/mobileWorkspace/mobileSideContent/MobileSideContent.tsx +++ b/src/commons/mobileWorkspace/mobileSideContent/MobileSideContent.tsx @@ -1,5 +1,5 @@ import { Classes, Icon, Tab, Tabs, Tooltip } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import { SideContentProps } from 'src/commons/sideContent/SideContent'; import { generateIconId } from 'src/commons/sideContent/SideContentHelper'; @@ -106,7 +106,7 @@ const MobileSideContent: React.FC = ({ onChange={onChange} renderActiveTabPanelOnly={renderActiveTabPanelOnly} selectedTabId={selectedTab} - className={classNames(Classes.DARK, 'mobile-side-content')} + className={clsx(Classes.DARK, 'mobile-side-content')} > {allTabs.map(tab => renderTab(tab, isIOS))} diff --git a/src/commons/mocks/StoreMocks.ts b/src/commons/mocks/StoreMocks.ts index 5bef6dde54..c60425c8f4 100644 --- a/src/commons/mocks/StoreMocks.ts +++ b/src/commons/mocks/StoreMocks.ts @@ -1,5 +1,5 @@ import { Store } from '@reduxjs/toolkit'; -import _ from 'lodash'; +import { isObject, mergeWith } from 'es-toolkit/compat'; import mockStore from 'redux-mock-store'; import { @@ -40,7 +40,7 @@ export function mockInitialStore( }; const lodashMergeCustomizer = (objValue: any, srcValue: any) => { - if (_.isObject(objValue)) { + if (isObject(objValue)) { return { ...objValue, // destination object ...srcValue // overrides @@ -48,5 +48,5 @@ export function mockInitialStore( } }; - return createStore(_.mergeWith(state, overrides, lodashMergeCustomizer)); + return createStore(mergeWith(state, overrides, lodashMergeCustomizer)); } diff --git a/src/commons/navigationBar/NavigationBar.tsx b/src/commons/navigationBar/NavigationBar.tsx index 1b803b442f..59fc161499 100644 --- a/src/commons/navigationBar/NavigationBar.tsx +++ b/src/commons/navigationBar/NavigationBar.tsx @@ -13,7 +13,7 @@ import { Position } from '@blueprintjs/core'; import { IconName, IconNames } from '@blueprintjs/icons'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useMemo, useState } from 'react'; import { Translation } from 'react-i18next'; import { Location, NavLink, Route, Routes, useLocation } from 'react-router'; @@ -272,7 +272,7 @@ const NavigationBar: React.FC = () => { - classNames('NavigationBar__link', Classes.BUTTON, Classes.MINIMAL, { + clsx('NavigationBar__link', Classes.BUTTON, Classes.MINIMAL, { [Classes.ACTIVE]: isActive }) } @@ -295,12 +295,7 @@ const NavigationBar: React.FC = () => { return ( <> {Constants.playgroundOnly ? isMobileBreakpoint @@ -357,7 +352,7 @@ export const DesktopNavLink: React.FC = props => { const shouldHide = props.hiddenInBreakpoints?.some(bp => responsive[bp]); return props.disabled ? null : ( classNames(isActive && Classes.ACTIVE)} + className={({ isActive }) => clsx(isActive && Classes.ACTIVE)} to={props.to} key={props.text} title={props.text} @@ -389,7 +384,7 @@ const MobileNavLink: React.FC< props.disabled ? null : ( classNames(isActive && Classes.ACTIVE)} + className={({ isActive }) => clsx(isActive && Classes.ACTIVE)} onClick={props.handleClick} key={props.text} > diff --git a/src/commons/repl/Repl.tsx b/src/commons/repl/Repl.tsx index 2a8416c6b5..17cf12470b 100644 --- a/src/commons/repl/Repl.tsx +++ b/src/commons/repl/Repl.tsx @@ -1,6 +1,6 @@ import { Card, Pre } from '@blueprintjs/core'; import { Ace } from 'ace-builds'; -import classNames from 'classnames'; +import clsx from 'clsx'; import { parseError } from 'js-slang'; import { Chapter, Variant } from 'js-slang/dist/types'; import { stringify } from 'js-slang/dist/utils/stringify'; @@ -52,7 +52,7 @@ const Repl: React.FC = props => {
{cards} {!props.inputHidden && ( - + )} diff --git a/src/commons/repl/ReplInput.tsx b/src/commons/repl/ReplInput.tsx index dc2a4e4bbc..770ba51484 100644 --- a/src/commons/repl/ReplInput.tsx +++ b/src/commons/repl/ReplInput.tsx @@ -1,6 +1,6 @@ import { Classes } from '@blueprintjs/core'; import { Ace } from 'ace-builds'; -import classNames from 'classnames'; +import clsx from 'clsx'; import { Chapter, Variant } from 'js-slang/dist/types'; import React from 'react'; import AceEditor from 'react-ace'; @@ -139,7 +139,7 @@ export const ReplInput: React.FC = props => { }} ref={replInput} /> -
{replButtons()}
+
{replButtons()}
{isDesktopBreakpoint &&
} ); diff --git a/src/commons/sagas/BackendSaga.ts b/src/commons/sagas/BackendSaga.ts index 19f0dc4ad3..2eb45f4c9c 100644 --- a/src/commons/sagas/BackendSaga.ts +++ b/src/commons/sagas/BackendSaga.ts @@ -1,6 +1,7 @@ /*eslint no-eval: "error"*/ /*eslint-env browser*/ -import _ from 'lodash'; +import { camelCase } from 'es-toolkit'; +import { mapKeys } from 'es-toolkit/compat'; import type { SagaIterator } from 'redux-saga'; import { all, call, fork, put, select } from 'redux-saga/effects'; import AcademyActions from 'src/features/academy/AcademyActions'; @@ -151,7 +152,7 @@ const newBackendSagaOne = combineSagaHandlers({ }, [SessionActions.handleSamlRedirect.type]: function* (action) { const { jwtCookie } = action.payload; - const tokens = _.mapKeys(JSON.parse(jwtCookie), (v, k) => _.camelCase(k)) as Tokens; + const tokens = mapKeys(JSON.parse(jwtCookie), (v, k) => camelCase(k)) as Tokens; yield put(actions.setTokens(tokens)); yield put(actions.fetchUserAndCourse()); diff --git a/src/commons/sagas/RemoteExecutionSaga.ts b/src/commons/sagas/RemoteExecutionSaga.ts index a481665c6e..3dcaf27c42 100644 --- a/src/commons/sagas/RemoteExecutionSaga.ts +++ b/src/commons/sagas/RemoteExecutionSaga.ts @@ -1,8 +1,8 @@ import { SlingClient } from '@sourceacademy/sling-client'; +import { pickBy } from 'es-toolkit/compat'; import { assemble, compileFiles, type Context } from 'js-slang'; import { ExceptionError } from 'js-slang/dist/errors/errors'; import { Chapter, Variant } from 'js-slang/dist/types'; -import _ from 'lodash'; import { call, put, race, select, take } from 'redux-saga/effects'; import RemoteExecutionActions from 'src/features/remoteExecution/RemoteExecutionActions'; import { @@ -126,7 +126,7 @@ const RemoteExecutionSaga = combineSagaHandlers({ device: { ...currentSession.device, peripherals: { - ..._.pickBy( + ...pickBy( currentSession.device.peripherals, p => Date.now() - p.lastUpdated < 3000 ), diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts index 2348afe6d3..00e8b76336 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts @@ -1,11 +1,11 @@ import { compileAndRun as compileAndRunCCode } from '@sourceacademy/c-slang/ctowasm/dist/index'; import { tokenizer } from 'acorn'; import type { IConduit } from 'conductor/dist/conduit'; +import { pick } from 'es-toolkit'; import { type Context, interrupt, type Result, resume, runFilesInContext } from 'js-slang'; import { ACORN_PARSE_OPTIONS } from 'js-slang/dist/constants'; import { InterruptedError } from 'js-slang/dist/errors/errors'; import { Chapter, ErrorSeverity, ErrorType, type SourceError, Variant } from 'js-slang/dist/types'; -import { pick } from 'lodash'; import { eventChannel, type SagaIterator } from 'redux-saga'; import { call, cancel, cancelled, fork, put, race, select, take } from 'redux-saga/effects'; import * as Sourceror from 'sourceror'; diff --git a/src/commons/sagas/WorkspaceSaga/helpers/runTestCase.ts b/src/commons/sagas/WorkspaceSaga/helpers/runTestCase.ts index 35452c49cd..1c58b09878 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/runTestCase.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/runTestCase.ts @@ -1,5 +1,5 @@ +import { random } from 'es-toolkit'; import type { Context } from 'js-slang'; -import { random } from 'lodash'; import { call, put, select, StrictEffect } from 'redux-saga/effects'; import type { OverallState } from '../../../application/ApplicationTypes'; diff --git a/src/commons/sideBar/SideBar.tsx b/src/commons/sideBar/SideBar.tsx index 39c9813dae..c516f4fd16 100644 --- a/src/commons/sideBar/SideBar.tsx +++ b/src/commons/sideBar/SideBar.tsx @@ -1,5 +1,5 @@ import { Card, Icon, IconName } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import { SideContentType } from '../sideContent/SideContentTypes'; @@ -52,7 +52,7 @@ const SideBar: React.FC = ({ tabs, isExpanded, expandSideBar, collapseSid {tabs.map((tab, index) => ( handleTabSelection(index)} diff --git a/src/commons/sideContent/SideContentHelper.ts b/src/commons/sideContent/SideContentHelper.ts index bd5ed857da..eae3788c61 100644 --- a/src/commons/sideContent/SideContentHelper.ts +++ b/src/commons/sideContent/SideContentHelper.ts @@ -1,9 +1,9 @@ import * as bpcore from '@blueprintjs/core'; import { TabId } from '@blueprintjs/core'; import * as bpicons from '@blueprintjs/icons'; +import compat from 'es-toolkit/compat'; import * as jsslang from 'js-slang'; import * as jsslangDist from 'js-slang/dist'; -import lodash from 'lodash'; import phaser from 'phaser'; import React, { useCallback } from 'react'; import JSXRuntime from 'react/jsx-runtime'; @@ -35,7 +35,7 @@ const requireProvider = (x: string) => { '@blueprintjs/icons': bpicons, 'js-slang': jsslang, 'js-slang/dist': jsslangDist, - lodash, + lodash: compat, phaser }; diff --git a/src/commons/sideContent/content/SideContentContestVoting.tsx b/src/commons/sideContent/content/SideContentContestVoting.tsx index 2af8989e25..8b9d8790f6 100644 --- a/src/commons/sideContent/content/SideContentContestVoting.tsx +++ b/src/commons/sideContent/content/SideContentContestVoting.tsx @@ -1,6 +1,6 @@ import { Button, Card, Classes, Collapse, Elevation, Icon, Pre, Tooltip } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ContestEntry } from '../../assessment/AssessmentTypes'; @@ -103,7 +103,7 @@ const SideContentContestVoting: React.FC = ({ const tierBoard = useMemo(() => { return TIERS.map((tier, index) => (
= ({ > {sortedContestEntries.map((contestEntry: ContestEntry, index) => (
overflow: this.state.visualization ? 'hidden' : 'auto' }} > -
+
-
+
{this.state.steps.length > 1 ? (
{step.length > 1 && (
Structure {i + 1} diff --git a/src/commons/sideContent/content/SideContentLeaderboardCard.tsx b/src/commons/sideContent/content/SideContentLeaderboardCard.tsx index f96b7b457c..3e330bef00 100644 --- a/src/commons/sideContent/content/SideContentLeaderboardCard.tsx +++ b/src/commons/sideContent/content/SideContentLeaderboardCard.tsx @@ -1,5 +1,5 @@ import { Card, Classes, Elevation, Pre } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import { ContestEntry } from '../../assessment/AssessmentTypes'; @@ -21,7 +21,7 @@ const SideContentLeaderboardCard: React.FC = ({ rank }) => { return ( -
+
= ({ index, result }) => { return (
diff --git a/src/commons/sideContent/content/SideContentSessionManagement.tsx b/src/commons/sideContent/content/SideContentSessionManagement.tsx index 99c1f307ba..6bc87e04fe 100644 --- a/src/commons/sideContent/content/SideContentSessionManagement.tsx +++ b/src/commons/sideContent/content/SideContentSessionManagement.tsx @@ -1,7 +1,7 @@ import { Classes, HTMLTable, Icon, Switch } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { CollabEditingAccess, type SharedbAceUser } from '@sourceacademy/sharedb-ace/types'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useEffect, useState } from 'react'; import CopyToClipboard from 'react-copy-to-clipboard'; import { useDispatch } from 'react-redux'; @@ -73,7 +73,7 @@ function AdminView({ users, playgroundCode }: AdminViewProps) { alignIndicator="left" checked={toggleAll} onChange={event => handleAllToggleAccess(event.target.checked)} - className={classNames(classes['switch'], classes['default-switch'])} + className={clsx(classes['switch'], classes['default-switch'])} />
@@ -84,7 +84,7 @@ function AdminView({ users, playgroundCode }: AdminViewProps) { alignIndicator="left" checked={defaultRole} onChange={event => handleDefaultToggleAccess(event.target.checked)} - className={classNames(classes['switch'], classes['default-switch'])} + className={clsx(classes['switch'], classes['default-switch'])} /> @@ -97,7 +97,7 @@ function AdminView({ users, playgroundCode }: AdminViewProps) { {Object.entries(users).map(([userId, user], index) => ( - +
{user.name}
@@ -184,14 +184,14 @@ const SideContentSessionManagement: React.FC = ({ {Object.values(users).map((user, index) => { return ( - +
{user.name}
- + {user.role === CollabEditingAccess.OWNER ? 'Admin' : user.role.charAt(0).toUpperCase() + user.role.slice(1)} diff --git a/src/commons/sideContent/content/SideContentSubstVisualizer.tsx b/src/commons/sideContent/content/SideContentSubstVisualizer.tsx index 5a8b04d42f..bd0e82a7f9 100644 --- a/src/commons/sideContent/content/SideContentSubstVisualizer.tsx +++ b/src/commons/sideContent/content/SideContentSubstVisualizer.tsx @@ -12,7 +12,7 @@ import { Slider } from '@blueprintjs/core'; import { getHotkeyHandler, HotkeyItem } from '@mantine/hooks'; -import classNames from 'classnames'; +import clsx from 'clsx'; import { HighlightRulesSelector, ModeSelector } from 'js-slang/dist/editors/ace/modes/source'; import { IStepperPropContents } from 'js-slang/dist/tracer'; import { StepperBaseNode } from 'js-slang/dist/tracer/interface'; @@ -161,7 +161,7 @@ const SideContentSubstVisualizer: React.FC = props => { return (
diff --git a/src/commons/sideContent/content/SideContentTestcaseCard.tsx b/src/commons/sideContent/content/SideContentTestcaseCard.tsx index a8342d6cb2..e66dfd2a80 100644 --- a/src/commons/sideContent/content/SideContentTestcaseCard.tsx +++ b/src/commons/sideContent/content/SideContentTestcaseCard.tsx @@ -1,5 +1,5 @@ import { Card, Classes, Elevation, Pre } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import { parseError } from 'js-slang'; import { stringify } from 'js-slang/dist/utils/stringify'; import React from 'react'; @@ -61,7 +61,7 @@ const SideContentTestcaseCard: React.FC = props => * be rendered in the GitHubAssessmentWorkspace for students. */ return ( -
+
{testcase.type === TestcaseTypes.opaque && props.workspaceLocation === 'assessment' ? ( // Render a placeholder cell in place of the actual testcase data for opaque testcases diff --git a/src/commons/sideContent/content/SideContentToneMatrix.tsx b/src/commons/sideContent/content/SideContentToneMatrix.tsx index 4c4a307d20..1991c1d0a2 100644 --- a/src/commons/sideContent/content/SideContentToneMatrix.tsx +++ b/src/commons/sideContent/content/SideContentToneMatrix.tsx @@ -1,5 +1,5 @@ import { Button, Classes } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { useEffect, useRef } from 'react'; const SideContentToneMatrix: React.FC = () => { @@ -22,7 +22,7 @@ const SideContentToneMatrix: React.FC = () => { return (
-
+
diff --git a/src/commons/sideContent/content/githubAssessments/SideContentEditableTestcaseCard.tsx b/src/commons/sideContent/content/githubAssessments/SideContentEditableTestcaseCard.tsx index 2407f595eb..1a7afc4431 100644 --- a/src/commons/sideContent/content/githubAssessments/SideContentEditableTestcaseCard.tsx +++ b/src/commons/sideContent/content/githubAssessments/SideContentEditableTestcaseCard.tsx @@ -1,6 +1,6 @@ import { Button, Card, Classes, Elevation, InputGroup } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import classNames from 'classnames'; +import clsx from 'clsx'; import { parseError } from 'js-slang'; import { stringify } from 'js-slang/dist/utils/stringify'; import React from 'react'; @@ -72,7 +72,7 @@ const SideContentEditableTestcaseCard: React.FC +
{ <> diff --git a/src/commons/sideContent/content/remoteExecution/SideContentRemoteExecution.tsx b/src/commons/sideContent/content/remoteExecution/SideContentRemoteExecution.tsx index 6f5c04d1d0..55ae7372d6 100644 --- a/src/commons/sideContent/content/remoteExecution/SideContentRemoteExecution.tsx +++ b/src/commons/sideContent/content/remoteExecution/SideContentRemoteExecution.tsx @@ -7,7 +7,7 @@ import { NonIdealState, Spinner } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React, { SetStateAction, useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { NavLink } from 'react-router'; @@ -150,7 +150,7 @@ const SideContentRemoteExecution: React.FC = pr
- + dispatch(actions.remoteExecDisconnect())} diff --git a/src/commons/sourceRecorder/SourceRecorderEditor.tsx b/src/commons/sourceRecorder/SourceRecorderEditor.tsx index 0f7b9af4d0..84f695707c 100644 --- a/src/commons/sourceRecorder/SourceRecorderEditor.tsx +++ b/src/commons/sourceRecorder/SourceRecorderEditor.tsx @@ -4,7 +4,7 @@ import 'js-slang/dist/editors/ace/theme/source'; import { Card } from '@blueprintjs/core'; import { Ace } from 'ace-builds'; -import { isEqual } from 'lodash'; +import { isEqual } from 'es-toolkit'; import React from 'react'; import AceEditor, { IAceEditorProps } from 'react-ace'; diff --git a/src/commons/sourceRecorder/SourceRecorderTable.tsx b/src/commons/sourceRecorder/SourceRecorderTable.tsx index 404a231d97..2ca6c112f6 100644 --- a/src/commons/sourceRecorder/SourceRecorderTable.tsx +++ b/src/commons/sourceRecorder/SourceRecorderTable.tsx @@ -10,7 +10,7 @@ import { } from '@blueprintjs/core'; import { ColDef, GridApi, GridReadyEvent } from 'ag-grid-community'; import { AgGridReact } from 'ag-grid-react'; -import { sortBy } from 'lodash'; +import { sortBy } from 'es-toolkit/compat'; import React from 'react'; import { PlaybackData, SourcecastData } from '../../features/sourceRecorder/SourceRecorderTypes'; diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index e588ca834d..190a9ef6b1 100644 --- a/src/commons/utils/JsSlangHelper.ts +++ b/src/commons/utils/JsSlangHelper.ts @@ -1,4 +1,6 @@ /* tslint:disable: ban-types*/ +import { difference } from 'es-toolkit'; +import { keys } from 'es-toolkit/compat'; import createSlangContext, { defineBuiltin, importBuiltins } from 'js-slang/dist/createContext'; import { type Chapter, @@ -9,7 +11,6 @@ import { Variant } from 'js-slang/dist/types'; import { stringify } from 'js-slang/dist/utils/stringify'; -import { difference, keys } from 'lodash'; import CseMachine from 'src/features/cseMachine/CseMachine'; import DataVisualizer from '../../features/dataVisualizer/dataVisualizer'; diff --git a/src/commons/utils/MemoizeHelper.ts b/src/commons/utils/MemoizeHelper.ts index da39edaa0a..9e7d17f4bc 100644 --- a/src/commons/utils/MemoizeHelper.ts +++ b/src/commons/utils/MemoizeHelper.ts @@ -1,4 +1,4 @@ -import * as _ from 'lodash'; +import { isEqual } from 'es-toolkit'; /** * Performs a deep comparison between the previous & next props state @@ -9,4 +9,4 @@ import * as _ from 'lodash'; * @param nextProps The next state of the props passed into a component */ export const propsAreEqual = (prevProps: T, nextProps: T): boolean => - _.isEqual(prevProps, nextProps); + isEqual(prevProps, nextProps); diff --git a/src/commons/utils/RequestHelper.tsx b/src/commons/utils/RequestHelper.tsx index 23c804d535..30203779ee 100644 --- a/src/commons/utils/RequestHelper.tsx +++ b/src/commons/utils/RequestHelper.tsx @@ -1,5 +1,5 @@ import { Button } from '@blueprintjs/core'; -import _ from 'lodash'; +import { cloneDeep } from 'es-toolkit'; import { assessmentFullPathRegex } from 'src/features/academy/AcademyTypes'; import { store } from 'src/pages/createStore'; @@ -87,7 +87,7 @@ export const request = async ( } store.dispatch(actions.setTokens(newTokens)); - const updatedFetchOptions = _.cloneDeep(fetchOptions); + const updatedFetchOptions = cloneDeep(fetchOptions); updatedFetchOptions.headers.set('Authorization', `Bearer ${newTokens.accessToken}`); const retriedResp = await fetch(`${Constants.backendUrl}/v2/${path}`, updatedFetchOptions); diff --git a/src/commons/workspace/__tests__/WorkspaceReducer.test.ts b/src/commons/workspace/__tests__/WorkspaceReducer.test.ts index b688749fd4..0625ccc2ad 100644 --- a/src/commons/workspace/__tests__/WorkspaceReducer.test.ts +++ b/src/commons/workspace/__tests__/WorkspaceReducer.test.ts @@ -1,5 +1,5 @@ +import { cloneDeep } from 'es-toolkit'; import { Chapter, Variant } from 'js-slang/dist/types'; -import { cloneDeep } from 'lodash'; import CommonsActions from 'src/commons/application/actions/CommonsActions'; import InterpreterActions from 'src/commons/application/actions/InterpreterActions'; import { diff --git a/src/features/cseMachine/CseMachineUtils.ts b/src/features/cseMachine/CseMachineUtils.ts index 6abea62ad1..26924d76be 100644 --- a/src/features/cseMachine/CseMachineUtils.ts +++ b/src/features/cseMachine/CseMachineUtils.ts @@ -1,3 +1,5 @@ +import { cloneDeep } from 'es-toolkit'; +import { isObject } from 'es-toolkit/compat'; import { estreeDecode } from 'js-slang/dist/alt-langs/scheme/scm-slang/src/utils/encoder-visitor'; import { unparse } from 'js-slang/dist/alt-langs/scheme/scm-slang/src/utils/reverse_parser'; import JsSlangClosure from 'js-slang/dist/cse-machine/closure'; @@ -18,7 +20,6 @@ import { Group } from 'konva/lib/Group'; import { Node } from 'konva/lib/Node'; import { Shape } from 'konva/lib/Shape'; import { Text } from 'konva/lib/shapes/Text'; -import { cloneDeep, isObject } from 'lodash'; import { isSchemeLanguage } from 'src/commons/application/ApplicationTypes'; import classes from 'src/styles/Draggable.module.scss'; diff --git a/src/features/game/chapter/GameChapterHelpers.ts b/src/features/game/chapter/GameChapterHelpers.ts index aad52ae7c4..cdc351bfe7 100644 --- a/src/features/game/chapter/GameChapterHelpers.ts +++ b/src/features/game/chapter/GameChapterHelpers.ts @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { sortBy } from 'es-toolkit/compat'; import { request } from 'src/commons/utils/RequestHelper'; import { store } from '../../../pages/createStore'; @@ -19,7 +19,7 @@ export async function fetchGameChapters(): Promise { }); if (!response) return []; const chapterDetails = response.status === 200 ? await response.json() : []; - const sortedChapters = _.sortBy(chapterDetails, chapterDetail => new Date(chapterDetail.openAt)); + const sortedChapters = sortBy(chapterDetails, chapterDetail => new Date(chapterDetail.openAt)); sortedChapters.forEach(chapter => (chapter.filenames = chapter.filenames.map(toTxtPath))); return sortedChapters; } diff --git a/src/features/game/save/GameSaveRequests.ts b/src/features/game/save/GameSaveRequests.ts index 149a7af37e..bec751382f 100644 --- a/src/features/game/save/GameSaveRequests.ts +++ b/src/features/game/save/GameSaveRequests.ts @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { isEmpty } from 'es-toolkit/compat'; import Constants from 'src/commons/utils/Constants'; import SourceAcademyGame from '../SourceAcademyGame'; @@ -44,7 +44,7 @@ export async function loadData(): Promise { const message = await resp.text(); const json = JSON.parse(message).courseRegistration?.gameStates; - return _.isEmpty(json) ? createEmptySaveState() : json; + return isEmpty(json) ? createEmptySaveState() : json; } /** diff --git a/src/features/game/utils/StyleUtils.ts b/src/features/game/utils/StyleUtils.ts index 071cc14dbd..003cecadc7 100644 --- a/src/features/game/utils/StyleUtils.ts +++ b/src/features/game/utils/StyleUtils.ts @@ -1,4 +1,5 @@ -import _ from 'lodash'; +import { mapValues } from 'es-toolkit'; +import { times } from 'es-toolkit/compat'; import { screenSize } from '../commons/CommonConstants'; @@ -21,7 +22,7 @@ export const Color = { }; const hex = (str: string) => parseInt(str.slice(1), 16); -export const HexColor = _.mapValues(Color, hex); +export const HexColor = mapValues(Color, hex); type TableFormatPosConfig = { direction?: Direction; @@ -64,7 +65,7 @@ export function calcTableFormatPos({ let itemsPerList = numItemLimit || numOfItems; const numOfLists = Math.ceil(numOfItems / itemsPerList); - return _.times(numOfItems, itemNumber => { + return times(numOfItems, itemNumber => { const itemIndexInList = itemNumber % itemsPerList; const listIndex = Math.floor(itemNumber / itemsPerList); diff --git a/src/features/gameSimulator/GameSimulatorService.ts b/src/features/gameSimulator/GameSimulatorService.ts index a8cafb305c..3a39daaebf 100644 --- a/src/features/gameSimulator/GameSimulatorService.ts +++ b/src/features/gameSimulator/GameSimulatorService.ts @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { sortBy } from 'es-toolkit/compat'; import { sendAdminStoryRequest, sendAssetRequest, sendStoryRequest } from './GameSimulatorRequest'; import { ChapterDetail } from './GameSimulatorTypes'; @@ -114,7 +114,7 @@ export async function uploadAssetToS3(file: File, folderName: string) { export async function fetchChapters(): Promise { const response = await sendStoryRequest('', 'GET'); const chapterDetails = response.status === 200 ? await response.json() : []; - return _.sortBy(chapterDetails, (chapterDetail: ChapterDetail) => new Date(chapterDetail.openAt)); + return sortBy(chapterDetails, (chapterDetail: ChapterDetail) => new Date(chapterDetail.openAt)); } /** diff --git a/src/features/remoteExecution/PeripheralContainer.tsx b/src/features/remoteExecution/PeripheralContainer.tsx index a02f8af07c..41f4053abe 100644 --- a/src/features/remoteExecution/PeripheralContainer.tsx +++ b/src/features/remoteExecution/PeripheralContainer.tsx @@ -1,5 +1,5 @@ import { Classes } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; type PeripheralContainerProps = { src: string | React.ReactElement; @@ -11,7 +11,7 @@ const PeripheralContainer: React.FC = ({ src, alt = 'I return (
{typeof src == 'string' ? {alt} : src} diff --git a/src/features/remoteExecution/RemoteExecutionDeviceDialog.tsx b/src/features/remoteExecution/RemoteExecutionDeviceDialog.tsx index 9b16045e64..ac42cc4c24 100644 --- a/src/features/remoteExecution/RemoteExecutionDeviceDialog.tsx +++ b/src/features/remoteExecution/RemoteExecutionDeviceDialog.tsx @@ -10,7 +10,7 @@ import { InputGroup, Tooltip } from '@blueprintjs/core'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import { QrReader } from 'react-qr-reader'; import { useDispatch } from 'react-redux'; @@ -112,7 +112,7 @@ const RemoteExecutionDeviceDialog: React.FC = ({ > = ({ void (typeField.ref.current = element)} disabled={isSubmitting || !!deviceToEdit} {...(deviceToEdit ? { value: deviceToEdit.type } : undefined)} @@ -144,7 +144,7 @@ const RemoteExecutionDeviceDialog: React.FC = ({ { ) : routeCourseId === courseId ? ( ) : ( -
+
| string[] | any; diff --git a/src/pages/academy/grading/subcomponents/GradingBadges.tsx b/src/pages/academy/grading/subcomponents/GradingBadges.tsx index 53d157afbb..e1a650ad3e 100644 --- a/src/pages/academy/grading/subcomponents/GradingBadges.tsx +++ b/src/pages/academy/grading/subcomponents/GradingBadges.tsx @@ -1,6 +1,6 @@ import { Icon } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import classNames from 'classnames'; +import clsx from 'clsx'; import React from 'react'; import { ProgressStatus, ProgressStatuses } from 'src/commons/assessment/AssessmentTypes'; import { ColumnFilter } from 'src/features/grading/GradingTypes'; @@ -21,7 +21,7 @@ type BadgeProps = { const Badge: React.FC = props => { return (
= props => { return ( {props.displayName} {!props.disabledSortCols.includes(props.column.getColId()) && (
nextSortState()} > @@ -50,7 +47,7 @@ const GradingColumnCustomHeaders: React.FC = props => { )}
props.hideColumn(props.column.getColId())} > diff --git a/src/pages/academy/grading/subcomponents/GradingSubmissionsTable.tsx b/src/pages/academy/grading/subcomponents/GradingSubmissionsTable.tsx index 72f9f1b18d..449c377149 100644 --- a/src/pages/academy/grading/subcomponents/GradingSubmissionsTable.tsx +++ b/src/pages/academy/grading/subcomponents/GradingSubmissionsTable.tsx @@ -5,8 +5,8 @@ import { Button, H6, Icon, InputGroup } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { CellClickedEvent, ColDef } from 'ag-grid-community'; import { AgGridReact } from 'ag-grid-react'; -import classNames from 'classnames'; -import { debounce } from 'lodash'; +import clsx from 'clsx'; +import { debounce } from 'es-toolkit'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useNavigate } from 'react-router'; @@ -141,7 +141,7 @@ const GradingSubmissionTable: React.FC = ({ "Hmm... we didn't find any submissions, you might want to debug your filter() function.", pageSize: pageSize, pagination: true, - rowClass: classNames(classes['grading-left-align'], classes['grading-table-rows']), + rowClass: clsx(classes['grading-left-align'], classes['grading-table-rows']), rowHeight: ROW_HEIGHT, suppressMenuHide: true, suppressPaginationPanel: true, @@ -374,7 +374,7 @@ const GradingSubmissionTable: React.FC = ({