diff --git a/webui/react/src/components/ActionSheet.module.scss b/webui/react/src/components/ActionSheet.module.scss index 96dcc98f88c..9f6372cce05 100644 --- a/webui/react/src/components/ActionSheet.module.scss +++ b/webui/react/src/components/ActionSheet.module.scss @@ -29,12 +29,10 @@ color: var(--theme-float-on); display: flex; flex-shrink: 0; + gap: 16px; height: var(--nav-bottom-bar-height); padding: 0 16px; - .icon { - padding-right: 16px; - } .label { font-size: 14px; } diff --git a/webui/react/src/components/ActionSheet.tsx b/webui/react/src/components/ActionSheet.tsx index 59f2fc0527e..e103c026cc6 100644 --- a/webui/react/src/components/ActionSheet.tsx +++ b/webui/react/src/components/ActionSheet.tsx @@ -42,11 +42,9 @@ const ActionSheet: React.FC = ({ onCancel, ...props }: Props) => { return ( {action.icon && typeof action.icon === 'string' ? ( -
- -
+ ) : ( -
{action.icon}
+ action.icon )} {!action.icon && }
{action.label}
@@ -76,9 +74,7 @@ const ActionSheet: React.FC = ({ onCancel, ...props }: Props) => { {!props.hideCancel && ( -
- -
+
Cancel
)} diff --git a/webui/react/src/components/AuthToken.tsx b/webui/react/src/components/AuthToken.tsx index a8a71f20c7f..207a6a23f43 100644 --- a/webui/react/src/components/AuthToken.tsx +++ b/webui/react/src/components/AuthToken.tsx @@ -1,7 +1,7 @@ -import { CopyOutlined } from '@ant-design/icons'; import React, { useCallback } from 'react'; import Button from 'components/kit/Button'; +import Icon from 'components/kit/Icon'; import Message from 'components/kit/Message'; import { makeToast } from 'components/kit/Toast'; import { globalStorage } from 'globalStorage'; @@ -29,7 +29,11 @@ const AuthToken: React.FC = () => { return ( } key="copy" type="primary" onClick={handleCopyToClipboard}> + } diff --git a/webui/react/src/components/DeterminedAuth.tsx b/webui/react/src/components/DeterminedAuth.tsx index 60e64ca6674..47665635a58 100644 --- a/webui/react/src/components/DeterminedAuth.tsx +++ b/webui/react/src/components/DeterminedAuth.tsx @@ -115,7 +115,7 @@ const DeterminedAuth: React.FC = ({ canceler }: Props) => { } + prefix={} /> diff --git a/webui/react/src/components/ExperimentIcons.tsx b/webui/react/src/components/ExperimentIcons.tsx new file mode 100644 index 00000000000..931ad1a645f --- /dev/null +++ b/webui/react/src/components/ExperimentIcons.tsx @@ -0,0 +1,55 @@ +import React, { useMemo } from 'react'; + +import Icon, { Props as IconProps, IconSize } from 'components/kit/Icon'; +import { stateToLabel } from 'constants/states'; +import { CompoundRunState, JobState, RunState } from 'types'; + +interface Props { + showTooltip?: boolean; + state: CompoundRunState; + size?: IconSize; + backgroundColor?: React.CSSProperties['backgroundColor']; + opacity?: React.CSSProperties['opacity']; +} + +const ExperimentIcons: React.FC = ({ + state, + showTooltip = true, + size, + backgroundColor, + opacity, +}) => { + const iconProps: IconProps = useMemo(() => { + switch (state) { + case JobState.SCHEDULED: + case JobState.SCHEDULEDBACKFILLED: + case JobState.QUEUED: + case RunState.Queued: + return { backgroundColor, name: 'queued', opacity, title: stateToLabel(state) }; + case RunState.Starting: + case RunState.Pulling: + return { name: 'spin-bowtie', title: stateToLabel(state) }; + case RunState.Running: + return { name: 'spin-shadow', title: stateToLabel(state) }; + case RunState.Paused: + return { color: 'cancel', name: 'pause', title: 'Paused' }; + case RunState.Completed: + return { color: 'success', name: 'checkmark', title: 'Completed' }; + case RunState.Error: + case RunState.Deleted: + case RunState.Deleting: + case RunState.DeleteFailed: + return { color: 'error', name: 'error', title: 'Error' }; + case RunState.Active: + case RunState.Unspecified: + case JobState.UNSPECIFIED: + return { name: 'active', title: stateToLabel(state) }; + default: + return { color: 'cancel', name: 'cancelled', title: 'Stopped' }; + } + }, [backgroundColor, opacity, state]); + + return ; +}; + +export default ExperimentIcons; diff --git a/webui/react/src/components/ExperimentIcons/Active.module.scss b/webui/react/src/components/ExperimentIcons/Active.module.scss deleted file mode 100644 index b10a37857d2..00000000000 --- a/webui/react/src/components/ExperimentIcons/Active.module.scss +++ /dev/null @@ -1,53 +0,0 @@ -@use 'sass:math'; - -.base { - align-items: center; - display: flex; - height: 24px; - justify-content: center; - width: 24px; -} - -@keyframes pulse { - 50% { - opacity: 0.5; - } -} - -.dots { - $duration: 1s; - $size: 3px; - - &, - &::before, - &::after { - animation-delay: math.div(-$duration, 3); - animation-duration: $duration; - animation-iteration-count: infinite; - animation-name: pulse; - animation-timing-function: ease-in-out; - border-radius: 50%; - box-shadow: 0 -1rem var(--theme-strong); - content: ''; - display: block; - height: $size; - position: relative; - top: 1rem; - width: $size; - } - &::before, - &::after { - height: 100%; - position: absolute; - top: 0; - width: 100%; - } - &::before { - animation-delay: math.div(-(2 * $duration), 3); - left: -0.5rem; - } - &::after { - animation-delay: 0s; - left: 0.5rem; - } -} diff --git a/webui/react/src/components/ExperimentIcons/Loader.module.scss b/webui/react/src/components/ExperimentIcons/Loader.module.scss deleted file mode 100644 index f1a979d548c..00000000000 --- a/webui/react/src/components/ExperimentIcons/Loader.module.scss +++ /dev/null @@ -1,57 +0,0 @@ -.loader { - border-spacing: 0.2em; - box-sizing: border-box; - display: inline-table; - height: 24px; - position: relative; - width: 24px; -} -.loader .row { - display: table-row; -} -.loader .row span { - animation: 0.5986 infinite alternate ease-out; - animation-name: flicker; - background: var(--theme-float-on); - display: table-cell; - opacity: 0; - position: relative; -} - -@keyframes flicker { - 0%, - 20% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.loader .row:nth-child(1) span:nth-child(1) { - animation-delay: 0.5s; -} -.loader .row:nth-child(1) span:nth-child(2) { - animation-delay: 0.7s; -} -.loader .row:nth-child(1) span:nth-child(3) { - animation-delay: 0.6s; -} -.loader .row:nth-child(2) span:nth-child(1) { - animation-delay: 0.2s; -} -.loader .row:nth-child(2) span:nth-child(2) { - animation-delay: 0.8s; -} -.loader .row:nth-child(2) span:nth-child(3) { - animation-delay: 0.9s; -} -.loader .row:nth-child(3) span:nth-child(1) { - animation-delay: 0.1s; -} -.loader .row:nth-child(3) span:nth-child(2) { - animation-delay: 0.3s; -} -.loader .row:nth-child(3) span:nth-child(3) { - animation-delay: 0.4s; -} diff --git a/webui/react/src/components/ExperimentIcons/Loader.tsx b/webui/react/src/components/ExperimentIcons/Loader.tsx deleted file mode 100644 index baf2ec8f48a..00000000000 --- a/webui/react/src/components/ExperimentIcons/Loader.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; - -import css from './Loader.module.scss'; - -const Loader: React.FC = () => { - return ( -
-
-
- - - -
-
- - - -
-
- - - -
-
-
- ); -}; - -export default Loader; diff --git a/webui/react/src/components/ExperimentIcons/Queue.module.scss b/webui/react/src/components/ExperimentIcons/Queue.module.scss deleted file mode 100644 index 0b23f8471fa..00000000000 --- a/webui/react/src/components/ExperimentIcons/Queue.module.scss +++ /dev/null @@ -1,42 +0,0 @@ -.base { - height: 24px; - margin: auto; - position: relative; - width: 24px; -} -.spinner, -.innerSpinner { - animation: pulse 1.75s infinite ease-in-out; - background-color: rgba(143 143 143 / 20%); - border-radius: 50%; - height: 100%; - left: 0; - opacity: 0.85; - position: absolute; - top: 0; - width: 100%; -} -.innerSpinner { - animation: pulseSpinner 1.75s infinite ease-in-out; - background-color: rgba(143 143 143 / 40%); -} - -@keyframes pulse { - 0%, - 100% { - transform: scale(1); - } - 50% { - transform: scale(0.75); - } -} - -@keyframes pulseSpinner { - 0%, - 100% { - transform: scale(0); - } - 50% { - transform: scale(0.6); - } -} diff --git a/webui/react/src/components/ExperimentIcons/Queue.tsx b/webui/react/src/components/ExperimentIcons/Queue.tsx deleted file mode 100644 index eede0285cb7..00000000000 --- a/webui/react/src/components/ExperimentIcons/Queue.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { CSSProperties, useMemo } from 'react'; - -import css from './Queue.module.scss'; - -interface Props { - // only height, width, opacity, and backgroundColor are available - style?: CSSProperties; -} - -const Queue: React.FC = ({ style }) => { - const spinnerStyle = useMemo(() => { - return { backgroundColor: style?.backgroundColor, opacity: style?.opacity }; - }, [style?.backgroundColor, style?.opacity]); - - return ( -
-
-
-
- ); -}; - -export default Queue; diff --git a/webui/react/src/components/ExperimentIcons/Spinner.module.scss b/webui/react/src/components/ExperimentIcons/Spinner.module.scss deleted file mode 100644 index 9e884fceef5..00000000000 --- a/webui/react/src/components/ExperimentIcons/Spinner.module.scss +++ /dev/null @@ -1,112 +0,0 @@ -@use 'sass:math'; - -$axes: X Y Z; -$size: 18px; - -.base { - align-items: center; - display: flex; - height: $size; - justify-content: center; - width: $size; -} - -@each $axis in $axes { - @keyframes rotate#{$axis} { - from { - transform: rotate#{$axis}#{'(0deg)'}; - } - to { - transform: rotate#{$axis}#{'(360deg)'}; - } - } -} - -@keyframes rotateHalf { - to { - transform: rotate(0.5turn); - } -} - -.spinner { - animation-duration: 0.75s; - animation-iteration-count: infinite; - animation-name: rotateZ; - animation-timing-function: linear; - border-color: var(--theme-ix-border); - border-radius: 50%; - border-style: solid; - border-width: 2px; - height: $size; - width: $size; -} -.spinner__bowtie { - animation: rotateHalf 1s infinite; - border-bottom-color: var(--theme-surface-on); - border-top-color: var(--theme-surface-on); -} -.spinner__half { - border-left-color: var(--theme-surface-on); - border-top-color: var(--theme-surface-on); -} - -$mask: conic-gradient(#0000 30%, #000), linear-gradient(#000 0 0) content-box; - -.spinner__shadow { - animation: rotateZ 0.75s infinite linear; - border-color: var(--theme-surface-on); - -webkit-mask: $mask; - mask: $mask; - -webkit-mask-composite: source-out; - mask-composite: subtract; -} -.spinner__split { - animation: none; - border: none; - box-shadow: inset 0 0 0 2px; - color: var(--theme-surface-on); - position: relative; - transform: scale(3); - - &::before, - &::after { - border-radius: 50%; - content: ''; - position: absolute; - } - &::before { - -webkit-animation: load2 2s infinite ease 1.5s; - animation: load2 2s infinite ease 1.5s; - background: white; - border-radius: 0; - height: $size; - left: 0; - top: 0; - -webkit-transform-origin: math.div($size, 2) math.div($size, 2); - transform-origin: math.div($size, 2) math.div($size, 2); - width: math.div($size, 2); - } - &::after { - -webkit-animation: load2 2s infinite ease; - animation: load2 2s infinite ease; - background: white; - border-radius: 0; - height: $size; - left: math.div($size, 2); - top: 0; - -webkit-transform-origin: 0 9px; - transform-origin: 0 math.div($size, 2); - width: math.div($size, 2); - } -} - -@keyframes load2 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} diff --git a/webui/react/src/components/ExperimentIcons/Spinner.tsx b/webui/react/src/components/ExperimentIcons/Spinner.tsx deleted file mode 100644 index 243f7a3eed6..00000000000 --- a/webui/react/src/components/ExperimentIcons/Spinner.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { CSSProperties } from 'react'; - -import css from './Spinner.module.scss'; - -interface Props { - style?: CSSProperties; - type: 'bowtie' | 'half' | 'split' | 'shadow'; -} - -const Spinner: React.FC = ({ type, style }) => { - const classnames = [css.spinner, css[`spinner__${type}`]]; - return ( -
-
-
- ); -}; - -export default Spinner; diff --git a/webui/react/src/components/ExperimentIcons/index.tsx b/webui/react/src/components/ExperimentIcons/index.tsx deleted file mode 100644 index fc8b11dd627..00000000000 --- a/webui/react/src/components/ExperimentIcons/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { CSSProperties, useMemo } from 'react'; - -import Icon from 'components/kit/Icon'; -import Tooltip from 'components/kit/Tooltip'; -import { stateToLabel } from 'constants/states'; -import { CompoundRunState, JobState, RunState } from 'types'; - -import Active from './Active'; -import Queue from './Queue'; -import Spinner from './Spinner'; - -interface Props { - showTooltip?: boolean; - state: CompoundRunState; - style?: CSSProperties; -} - -const ExperimentIcons: React.FC = ({ state, style, showTooltip = true }) => { - const icon = useMemo(() => { - switch (state) { - case JobState.SCHEDULED: - case JobState.SCHEDULEDBACKFILLED: - case JobState.QUEUED: - case RunState.Queued: - return ; - case RunState.Starting: - case RunState.Pulling: - return ; - case RunState.Running: - return ; - case RunState.Paused: - return ; - case RunState.Completed: - return ; - case RunState.Error: - case RunState.Deleted: - case RunState.Deleting: - case RunState.DeleteFailed: - return ; - case RunState.Active: - case RunState.Unspecified: - case JobState.UNSPECIFIED: - return ; - default: - return ; - } - }, [state, style]); - - return ( - <> - {showTooltip ? ( - -
{icon}
-
- ) : ( -
{icon}
- )} - - ); -}; - -export default ExperimentIcons; diff --git a/webui/react/src/components/FilterForm/components/FilterField.tsx b/webui/react/src/components/FilterForm/components/FilterField.tsx index c383f102fff..80802f03f36 100644 --- a/webui/react/src/components/FilterForm/components/FilterField.tsx +++ b/webui/react/src/components/FilterForm/components/FilterField.tsx @@ -1,4 +1,3 @@ -import { HolderOutlined } from '@ant-design/icons'; import { type SelectProps as AntdSelectProps } from 'antd'; import type { DatePickerProps } from 'antd/es/date-picker'; import dayjs from 'dayjs'; @@ -275,13 +274,13 @@ const FilterField = ({ )}
diff --git a/webui/react/src/components/FilterForm/components/FilterGroup.tsx b/webui/react/src/components/FilterForm/components/FilterGroup.tsx index 0b209095553..6cd2e5f2f12 100644 --- a/webui/react/src/components/FilterForm/components/FilterGroup.tsx +++ b/webui/react/src/components/FilterForm/components/FilterGroup.tsx @@ -1,4 +1,3 @@ -import { HolderOutlined, PlusOutlined } from '@ant-design/icons'; import { Dropdown, DropDownProps, type MenuProps } from 'antd'; import { useMemo, useRef } from 'react'; import { useDrag, useDrop } from 'react-dnd'; @@ -143,15 +142,15 @@ const FilterGroup = ({ disabled={group.children.length > ITEM_LIMIT} menu={menuItems} trigger={['click']}> -
)} diff --git a/webui/react/src/components/Logo.tsx b/webui/react/src/components/Logo.tsx index 4e836610c87..c8449fcbc14 100644 --- a/webui/react/src/components/Logo.tsx +++ b/webui/react/src/components/Logo.tsx @@ -14,16 +14,16 @@ import { reactHostAddress } from 'utils/routes'; import css from './Logo.module.scss'; -export const Orientation = { +const Orientation = { Horizontal: 'horizontal', Vertical: 'vertical', } as const; -export type Orientation = ValueOf; +type Orientation = ValueOf; interface Props { - branding: BrandingType; - orientation: Orientation; + branding?: BrandingType; + orientation?: Orientation; } const logos: Record>> = { @@ -49,7 +49,10 @@ const logos: Record> }, }; -const Logo: React.FC = ({ branding, orientation }: Props) => { +const Logo: React.FC = ({ + branding = BrandingType.Determined, + orientation = Orientation.Vertical, +}: Props) => { const { ui } = useUI(); const classes = [css[branding], css[orientation]]; diff --git a/webui/react/src/components/Metadata/MetadataCard.tsx b/webui/react/src/components/Metadata/MetadataCard.tsx index e78c1c98499..0c174429f37 100644 --- a/webui/react/src/components/Metadata/MetadataCard.tsx +++ b/webui/react/src/components/Metadata/MetadataCard.tsx @@ -1,10 +1,9 @@ -import { EditOutlined } from '@ant-design/icons'; import { Card, Space } from 'antd'; import React, { useCallback, useMemo, useState } from 'react'; import Button from 'components/kit/Button'; +import Icon from 'components/kit/Icon'; import Spinner from 'components/kit/Spinner'; -import Tooltip from 'components/kit/Tooltip'; import { Metadata } from 'types'; import handleError, { ErrorType } from 'utils/error'; @@ -71,9 +70,11 @@ const MetadataCard: React.FC = ({ disabled = false, metadata = {}, onSave ) : ( disabled || ( - - - +
))}
- @@ -230,7 +245,18 @@ const ModelCreateModal = ({ onClose, workspaceId }: Props): JSX.Element => { ]}>
- remove(name)} /> + diff --git a/webui/react/src/components/NavigationSideBar.module.scss b/webui/react/src/components/NavigationSideBar.module.scss index 5ce6a5d3b68..366702201a2 100644 --- a/webui/react/src/components/NavigationSideBar.module.scss +++ b/webui/react/src/components/NavigationSideBar.module.scss @@ -128,13 +128,13 @@ cursor: pointer; display: flex; flex-grow: 1; + gap: 16px; height: 48px; max-width: 100%; - .icon { + &:first-child { min-width: 56px; - padding-inline: 16px; - text-align: center; + padding-left: 16px; } .label { font-size: 13px; diff --git a/webui/react/src/components/NavigationSideBar.tsx b/webui/react/src/components/NavigationSideBar.tsx index a031f0568cf..7c724f43fd2 100644 --- a/webui/react/src/components/NavigationSideBar.tsx +++ b/webui/react/src/components/NavigationSideBar.tsx @@ -89,11 +89,9 @@ export const NavigationItem: React.FC = ({
{typeof props.icon === 'string' ? ( -
- -
+ ) : ( -
{props.icon}
+ props.icon )}
{props.labelRender ? props.labelRender : props.label}
@@ -292,7 +290,7 @@ const NavigationSideBar: React.FC = () => { {canCreateWorkspace && ( - */ - - - - } + {/* + * TODO: Add notebook integration + * + */} + {readonly && file !== NotLoaded && ( +
diff --git a/webui/react/src/components/kit/CodeEditor/CodeEditor.module.scss b/webui/react/src/components/kit/CodeEditor/CodeEditor.module.scss index 1ebffb293c9..79897473543 100644 --- a/webui/react/src/components/kit/CodeEditor/CodeEditor.module.scss +++ b/webui/react/src/components/kit/CodeEditor/CodeEditor.module.scss @@ -38,7 +38,7 @@ border-radius: 0; display: flex; justify-content: space-between; - padding: 0.5em 1em; + padding: 0.25em 1em; .buttonContainer { align-items: center; @@ -58,13 +58,6 @@ } .buttonsContainer { display: flex; - - .noBorderButton { - border: 0; - cursor: pointer; - margin-right: -4px; - width: 20px; - } } } } diff --git a/webui/react/src/components/kit/Icon.module.scss b/webui/react/src/components/kit/Icon.module.scss deleted file mode 100644 index 20ff5e680d2..00000000000 --- a/webui/react/src/components/kit/Icon.module.scss +++ /dev/null @@ -1,95 +0,0 @@ -.base { - align-items: center; - display: inline-flex; - user-select: none; - width: fit-content; -} -.tiny { - font-size: var(--icon-tiny); - - svg { - width: var(--icon-tiny); - } -} -.small { - font-size: var(--icon-small); - - svg { - width: var(--icon-small); - } -} -.medium { - font-size: var(--icon-medium); - - svg { - width: var(--icon-medium); - } -} -.large { - font-size: var(--icon-large); - - svg { - width: var(--icon-large); - } -} -.big { - font-size: var(--icon-big); - - svg { - width: var(--icon-big); - } -} -.great { - font-size: var(--icon-great); - - svg { - width: var(--icon-great); - } -} -.huge { - font-size: var(--icon-huge); - - svg { - width: var(--icon-huge); - } -} -.enormous { - font-size: var(--icon-enormous); - - svg { - width: var(--icon-enormous); - } -} -.giant { - font-size: var(--icon-giant); - - svg { - width: var(--icon-giant); - } -} -.jumbo { - font-size: var(--icon-jumbo); - - svg { - width: var(--icon-jumbo); - } -} -.mega { - font-size: var(--icon-mega); - - svg { - width: var(--icon-mega); - } -} -.cancel { - color: var(--theme-ix-cancel); -} -.error { - color: var(--theme-status-error); -} -.success { - color: var(--theme-status-success); -} -.filter { - margin-top: 1px; -} diff --git a/webui/react/src/components/kit/Icon.tsx b/webui/react/src/components/kit/Icon.tsx index 58df8df6162..6af7b3c0dce 100644 --- a/webui/react/src/components/kit/Icon.tsx +++ b/webui/react/src/components/kit/Icon.tsx @@ -1,20 +1,98 @@ +import { + ExclamationCircleOutlined, + HolderOutlined, + MinusCircleOutlined, + ProjectOutlined, +} from '@ant-design/icons'; import React, { useMemo } from 'react'; import Tooltip from 'components/kit/Tooltip'; -import css from './Icon.module.scss'; -import ColumnsIcon from './icons/ColumnsIcon.svg'; -import FilterIcon from './icons/FilterIcon.svg'; -import FourSquaresIcon from './icons/FourSquaresIcon.svg'; -import HeatmapIcon from './icons/HeatmapIcon.svg'; -import OptionsIcon from './icons/OptionsIcon.svg'; -import PanelIcon from './icons/PanelIcon.svg'; -import PanelOnIcon from './icons/PanelOnIcon.svg'; -import RowIconExtraLarge from './icons/RowIconExtraLarge.svg'; -import RowIconLarge from './icons/RowIconLarge.svg'; -import RowIconMedium from './icons/RowIconMedium.svg'; -import RowIconSmall from './icons/RowIconSmall.svg'; -import ScrollIcon from './icons/ScrollIcon.svg'; +import ActiveIcon from './Icon/Active'; +import css from './Icon/Icon.module.scss'; +import QueuedIcon from './Icon/Queue'; +import { SpinBowtie, SpinHalf, SpinShadow } from './Icon/Spin'; +import AddIcon from './icons/add.svg'; +import ArchiveIcon from './icons/archive.svg'; +import ArrowDownIcon from './icons/arrow-down.svg'; +import ArrowLeftIcon from './icons/arrow-left.svg'; +import ArrowRightIcon from './icons/arrow-right.svg'; +import ArrowUpIcon from './icons/arrow-up.svg'; +import CancelledIcon from './icons/cancelled.svg'; +import CheckmarkIcon from './icons/checkmark.svg'; +import CheckpointIcon from './icons/checkpoint.svg'; +import ClipboardIcon from './icons/clipboard.svg'; +import CloseIcon from './icons/close.svg'; +import CloudIcon from './icons/cloud.svg'; +import ClusterIcon from './icons/cluster.svg'; +import CollapseIcon from './icons/collapse.svg'; +import ColumnsIcon from './icons/columns.svg'; +import CommandIcon from './icons/command.svg'; +import DaiLogoIcon from './icons/dai-logo.svg'; +import DashboardIcon from './icons/dashboard.svg'; +import DebugIcon from './icons/debug.svg'; +import DocsIcon from './icons/docs.svg'; +import DocumentIcon from './icons/document.svg'; +import DownloadIcon from './icons/download.svg'; +import ErrorIcon from './icons/error.svg'; +import ExpandIcon from './icons/expand.svg'; +import ExperimentIcon from './icons/experiment.svg'; +import EyeCloseIcon from './icons/eye-close.svg'; +import EyeOpenIcon from './icons/eye-open.svg'; +import FilterIcon from './icons/filter.svg'; +import ForkIcon from './icons/fork.svg'; +import FourSquaresIcon from './icons/four-squares.svg'; +import FullscreenIcon from './icons/fullscreen.svg'; +import GridIcon from './icons/grid.svg'; +import GroupIcon from './icons/group.svg'; +import HeatIcon from './icons/heat.svg'; +import HeatmapIcon from './icons/heatmap.svg'; +import HomeIcon from './icons/home.svg'; +import InfoIcon from './icons/info.svg'; +import JupyterLabIcon from './icons/jupyter-lab.svg'; +import LearningIcon from './icons/learning.svg'; +import ListIcon from './icons/list.svg'; +import LockIcon from './icons/lock.svg'; +import LogsIcon from './icons/logs.svg'; +import ModelIcon from './icons/model.svg'; +import NotebookIcon from './icons/notebook.svg'; +import OptionsIcon from './icons/options.svg'; +import OverflowHorizontalIcon from './icons/overflow-horizontal.svg'; +import OverflowVerticalIcon from './icons/overflow-vertical.svg'; +import PanelOnIcon from './icons/panel-on.svg'; +import PanelIcon from './icons/panel.svg'; +import ParcoordsIcon from './icons/parcoords.svg'; +import PauseIcon from './icons/pause.svg'; +import PencilIcon from './icons/pencil.svg'; +import PinIcon from './icons/pin.svg'; +import PlayIcon from './icons/play.svg'; +import PopoutIcon from './icons/popout.svg'; +import PowerIcon from './icons/power.svg'; +import QueueIcon from './icons/queue.svg'; +import ResetIcon from './icons/reset.svg'; +import RowExtraLargeIcon from './icons/row-extra-large.svg'; +import RowLargeIcon from './icons/row-large.svg'; +import RowMediumIcon from './icons/row-medium.svg'; +import RowSmallIcon from './icons/row-small.svg'; +import ScatterPlotIcon from './icons/scatter-plot.svg'; +import ScrollIcon from './icons/scroll.svg'; +import SearchIcon from './icons/search.svg'; +import SearcherAdaptiveIcon from './icons/searcher-adaptive.svg'; +import SearcherGridIcon from './icons/searcher-grid.svg'; +import SearcherRandomIcon from './icons/searcher-random.svg'; +import SettingsIcon from './icons/settings.svg'; +import ShellIcon from './icons/shell.svg'; +import SpinnerIcon from './icons/spinner.svg'; +import StarIcon from './icons/star.svg'; +import StopIcon from './icons/stop.svg'; +import TasksIcon from './icons/tasks.svg'; +import TensorBoardIcon from './icons/tensor-board.svg'; +import TensorboardIcon from './icons/tensorboard.svg'; +import UndoIcon from './icons/undo.svg'; +import UserIcon from './icons/user.svg'; +import WarningIcon from './icons/warning.svg'; +import WorkspacesIcon from './icons/workspaces.svg'; +import { XOR } from './internal/types'; export const IconSizeArray = [ 'tiny', @@ -32,20 +110,29 @@ export const IconSizeArray = [ export type IconSize = (typeof IconSizeArray)[number]; -const fontIcons = [ +export const svgIcons = [ + 'columns', + 'filter', + 'four-squares', + 'options', + 'panel', + 'panel-on', + 'row-large', + 'row-medium', + 'row-small', + 'row-xl', + 'heatmap', + 'scroll', 'home', 'dai-logo', 'arrow-left', 'arrow-right', - 'add-small', - 'close-small', + 'add', 'search', 'arrow-down', 'arrow-up', 'cancelled', 'group', - 'warning-large', - 'steering-wheel', 'workspaces', 'archive', 'queue', @@ -84,7 +171,6 @@ const fontIcons = [ 'user', 'jupyter-lab', 'lock', - 'user-small', 'popout', 'spinner', 'collapse', @@ -108,88 +194,164 @@ const fontIcons = [ 'trace', 'webhooks', 'external', + 'pin', ] as const; -type FontIconName = (typeof fontIcons)[number]; - -export const svgIcons = [ - 'columns', - 'filter', - 'four-squares', - 'options', - 'panel', - 'panel-on', - 'row-large', - 'row-medium', - 'row-small', - 'row-xl', - 'heatmap', - 'scroll', -] as const; - -type SvgIconName = (typeof svgIcons)[number]; - -export const IconNameArray = [...fontIcons, ...svgIcons]; - -export type IconName = (typeof IconNameArray)[number]; - -// intersection here is to ensure the index access in the component returns -// undefined | React.FC and not any -const svgIconMap: Record & { - [x in FontIconName]?: never; -} = { +const svgIconMap: Partial> = { + 'add': AddIcon, + 'archive': ArchiveIcon, + 'arrow-down': ArrowDownIcon, + 'arrow-left': ArrowLeftIcon, + 'arrow-right': ArrowRightIcon, + 'arrow-up': ArrowUpIcon, + 'cancelled': CancelledIcon, + 'checkmark': CheckmarkIcon, + 'checkpoint': CheckpointIcon, + 'clipboard': ClipboardIcon, + 'close': CloseIcon, + 'cloud': CloudIcon, + 'cluster': ClusterIcon, + 'collapse': CollapseIcon, 'columns': ColumnsIcon, + 'command': CommandIcon, + 'critical': ErrorIcon, // duplicate of error + 'dai-logo': DaiLogoIcon, + 'dashboard': DashboardIcon, + 'debug': DebugIcon, + 'docs': DocsIcon, + 'document': DocumentIcon, + 'download': DownloadIcon, + 'error': ErrorIcon, + 'expand': ExpandIcon, + 'experiment': ExperimentIcon, + 'external': GroupIcon, // duplicate of group + 'eye-close': EyeCloseIcon, + 'eye-open': EyeOpenIcon, 'filter': FilterIcon, + 'fork': ForkIcon, 'four-squares': FourSquaresIcon, + 'fullscreen': FullscreenIcon, + 'grid': GridIcon, + 'group': GroupIcon, + 'heat': HeatIcon, 'heatmap': HeatmapIcon, + 'home': HomeIcon, + 'info': InfoIcon, + 'jupyter-lab': JupyterLabIcon, + 'learning': LearningIcon, + 'list': ListIcon, + 'lock': LockIcon, + 'logs': LogsIcon, + 'model': ModelIcon, + 'notebook': NotebookIcon, 'options': OptionsIcon, + 'overflow-horizontal': OverflowHorizontalIcon, + 'overflow-vertical': OverflowVerticalIcon, 'panel': PanelIcon, 'panel-on': PanelOnIcon, - 'row-large': RowIconLarge, - 'row-medium': RowIconMedium, - 'row-small': RowIconSmall, - 'row-xl': RowIconExtraLarge, + 'parcoords': ParcoordsIcon, + 'pause': PauseIcon, + 'pencil': PencilIcon, + 'pin': PinIcon, + 'play': PlayIcon, + 'popout': PopoutIcon, + 'power': PowerIcon, + 'queue': QueueIcon, + 'reset': ResetIcon, + 'row-large': RowLargeIcon, + 'row-medium': RowMediumIcon, + 'row-small': RowSmallIcon, + 'row-xl': RowExtraLargeIcon, + 'scatter-plot': ScatterPlotIcon, 'scroll': ScrollIcon, + 'search': SearchIcon, + 'searcher-adaptive': SearcherAdaptiveIcon, + 'searcher-grid': SearcherGridIcon, + 'searcher-random': SearcherRandomIcon, + 'settings': SettingsIcon, + 'shell': ShellIcon, + 'spinner': SpinnerIcon, + 'star': StarIcon, + 'stop': StopIcon, + 'tasks': TasksIcon, + 'tensor-board': TensorBoardIcon, + 'tensorboard': TensorboardIcon, + 'trace': DaiLogoIcon, // duplicate of dai-logo + 'undo': UndoIcon, + 'user': UserIcon, + 'warning': WarningIcon, + 'webhooks': SearcherRandomIcon, // duplicate of searcher-random + 'workspaces': WorkspacesIcon, +} as const; + +const antdIcons = ['exclamation-circle', 'holder', 'minus-circle', 'project'] as const; + +const antdIconMap: Partial> = { + 'exclamation-circle': ExclamationCircleOutlined, + 'holder': HolderOutlined, + 'minus-circle': MinusCircleOutlined, + 'project': ProjectOutlined, +}; + +const componentIcons = ['active', 'spin-bowtie', 'spin-half', 'spin-shadow', 'queued'] as const; + +const componentIconMap: Partial> = { + 'active': ActiveIcon, + 'queued': QueuedIcon, + 'spin-bowtie': SpinBowtie, + 'spin-half': SpinHalf, + 'spin-shadow': SpinShadow, }; +export const IconNameArray = [...svgIcons, ...antdIcons, ...componentIcons]; + +export type IconName = (typeof IconNameArray)[number]; + type CommonProps = { color?: 'cancel' | 'error' | 'success'; - name: IconName; size?: IconSize; showTooltip?: boolean; + name: IconName; + backgroundColor?: React.CSSProperties['backgroundColor']; // currently only supported by Queued + opacity?: React.CSSProperties['opacity']; // currently only supported by Queued }; export type Props = CommonProps & - ( - | { - title: string; - decorative?: never; - } - | { - decorative: true; - } - ); -const Icon: React.FC = (props: Props) => { - const { name, size = 'medium', color } = props; - const showTooltip = 'decorative' in props ? false : props.showTooltip ?? false; - const title = 'decorative' in props ? undefined : props.title; - const decorative = 'decorative' in props; + XOR< + { + title: string; + }, + { + decorative: true; + } + >; +const Icon: React.FC = ({ + name, + size = 'medium', + color, + decorative, + title, + showTooltip = false, + backgroundColor, + opacity, +}: Props) => { const classes = [css.base]; - const svgIcon = useMemo(() => { - const MappedIcon = svgIconMap[name]; + const iconComponent = useMemo(() => { + if (name === 'queued') + return ; + const MappedIcon = svgIconMap[name] ?? antdIconMap[name] ?? componentIconMap[name]; return MappedIcon && ; - }, [name]); + }, [backgroundColor, name, opacity]); - if (name) classes.push(`icon-${name}`); if (size) classes.push(css[size]); if (color) classes.push(css[color]); const icon = ( - - {svgIcon} + + {iconComponent} ); - return showTooltip ? {icon} : icon; + return showTooltip && !decorative ? {icon} : icon; }; export default Icon; diff --git a/webui/react/src/components/kit/Icon/Active.module.scss b/webui/react/src/components/kit/Icon/Active.module.scss new file mode 100644 index 00000000000..165933f7c73 --- /dev/null +++ b/webui/react/src/components/kit/Icon/Active.module.scss @@ -0,0 +1,52 @@ +@use 'sass:math'; + +.base { + align-items: center; + display: flex; + justify-content: center; + width: 100%; + + .dots { + $duration: 1s; + $size: 3px; + + &, + &::before, + &::after { + animation-delay: math.div(-$duration, 3); + animation-duration: $duration; + animation-iteration-count: infinite; + animation-name: pulse; + animation-timing-function: ease-in-out; + border-radius: 50%; + box-shadow: 0 -1rem var(--theme-strong); + content: ''; + display: block; + height: $size; + position: relative; + top: 1rem; + width: $size; + } + &::before, + &::after { + height: 100%; + position: absolute; + top: 0; + width: 100%; + } + &::before { + animation-delay: math.div(-(2 * $duration), 3); + left: -0.5rem; + } + &::after { + animation-delay: 0s; + left: 0.5rem; + } + } +} + +@keyframes pulse { + 50% { + opacity: 0.5; + } +} diff --git a/webui/react/src/components/ExperimentIcons/Active.tsx b/webui/react/src/components/kit/Icon/Active.tsx similarity index 100% rename from webui/react/src/components/ExperimentIcons/Active.tsx rename to webui/react/src/components/kit/Icon/Active.tsx diff --git a/webui/react/src/components/kit/Icon/Icon.module.scss b/webui/react/src/components/kit/Icon/Icon.module.scss new file mode 100644 index 00000000000..4af9e9977c9 --- /dev/null +++ b/webui/react/src/components/kit/Icon/Icon.module.scss @@ -0,0 +1,53 @@ +.base { + align-items: center; + aspect-ratio: 1 / 1; + display: inline-flex !important; + fill: currentColor; + flex-shrink: 0; + user-select: none; +} +.tiny { + width: var(--icon-tiny); +} +.small { + width: var(--icon-small); +} +.medium { + width: var(--icon-medium); +} +.large { + width: var(--icon-large); +} +.big { + width: var(--icon-big); +} +.great { + width: var(--icon-great); +} +.huge { + width: var(--icon-huge); +} +.enormous { + width: var(--icon-enormous); +} +.giant { + width: var(--icon-giant); +} +.jumbo { + width: var(--icon-jumbo); +} +.mega { + width: var(--icon-mega); +} +.cancel { + color: var(--theme-ix-cancel); +} +.error { + color: var(--theme-status-error); +} +.success { + color: var(--theme-status-success); +} +.filter { + margin-top: 1px; +} diff --git a/webui/react/src/components/kit/Icon.test.tsx b/webui/react/src/components/kit/Icon/Icon.test.tsx similarity index 79% rename from webui/react/src/components/kit/Icon.test.tsx rename to webui/react/src/components/kit/Icon/Icon.test.tsx index c7e75619b84..4f6e0fd52ad 100644 --- a/webui/react/src/components/kit/Icon.test.tsx +++ b/webui/react/src/components/kit/Icon/Icon.test.tsx @@ -1,7 +1,7 @@ import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import Icon, { IconNameArray, IconSizeArray, type Props, svgIcons } from './Icon'; +import Icon, { IconNameArray, IconSizeArray, type Props, svgIcons } from 'components/kit/Icon'; const setup = (props?: Props) => { const user = userEvent.setup(); @@ -24,19 +24,18 @@ describe('Icon', () => { it.each(IconSizeArray)('should display a %s-size icon', (size) => { const { view } = setup({ name: 'star', size, title: size }); const firstChild = view.container.firstChild; - expect(firstChild).toHaveClass(...['base', 'icon-star', size]); + expect(firstChild).toHaveClass(size); }); }); describe('Name of icon', () => { - // todo: wanna test pseudo-element `content` value, but cannot find a way to test it + // TODO: figure out how to test which icon is displayed it.each(IconNameArray)('should display a %s icon', (name) => { const { view } = setup({ name, title: name }); const firstChild = view.container.firstChild; - if (!(svgIcons as readonly string[]).includes(name)) { - expect(firstChild).toHaveClass(...['base', `icon-${name}`, 'medium']); - } else { + if ((svgIcons as readonly string[]).includes(name)) { expect(firstChild?.firstChild?.nodeName).toBe('svg'); + expect(view.getByLabelText(name)).toBeVisible(); } }); }); diff --git a/webui/react/src/components/kit/Icon/Queue.module.scss b/webui/react/src/components/kit/Icon/Queue.module.scss new file mode 100644 index 00000000000..8db790a768c --- /dev/null +++ b/webui/react/src/components/kit/Icon/Queue.module.scss @@ -0,0 +1,43 @@ +.base { + height: 100%; + margin: auto; + position: relative; + width: 100%; + + .spinner, + .innerSpinner { + animation: pulse 1.75s infinite ease-in-out; + background-color: rgba(143 143 143 / 20%); + border-radius: 50%; + height: 100%; + left: 0; + opacity: 0.85; + position: absolute; + top: 0; + width: 100%; + } + .innerSpinner { + animation: pulseSpinner 1.75s infinite ease-in-out; + background-color: rgba(143 143 143 / 40%); + } +} + +@keyframes pulse { + 0%, + 100% { + transform: scale(1); + } + 50% { + transform: scale(0.75); + } +} + +@keyframes pulseSpinner { + 0%, + 100% { + transform: scale(0); + } + 50% { + transform: scale(0.6); + } +} diff --git a/webui/react/src/components/kit/Icon/Queue.tsx b/webui/react/src/components/kit/Icon/Queue.tsx new file mode 100644 index 00000000000..ef88a05416a --- /dev/null +++ b/webui/react/src/components/kit/Icon/Queue.tsx @@ -0,0 +1,23 @@ +import React, { CSSProperties, useMemo } from 'react'; + +import css from './Queue.module.scss'; + +interface Props { + backgroundColor?: CSSProperties['backgroundColor']; + opacity?: CSSProperties['opacity']; +} + +const Queue: React.FC = ({ backgroundColor, opacity }) => { + const spinnerStyle = useMemo(() => { + return { backgroundColor, opacity }; + }, [backgroundColor, opacity]); + + return ( +
+
+
+
+ ); +}; + +export default Queue; diff --git a/webui/react/src/components/kit/Icon/Spin.module.scss b/webui/react/src/components/kit/Icon/Spin.module.scss new file mode 100644 index 00000000000..fbf442c890d --- /dev/null +++ b/webui/react/src/components/kit/Icon/Spin.module.scss @@ -0,0 +1,61 @@ +@use 'sass:math'; + +$axes: X Y Z; +$size: 100%; +$mask: conic-gradient(#0000 30%, #000), linear-gradient(#000 0 0) content-box; + +.base { + align-items: center; + border-color: currentColor; + display: flex; + height: $size; + justify-content: center; + width: $size; + + .spinner { + animation-duration: 0.75s; + animation-iteration-count: infinite; + animation-name: rotateZ; + animation-timing-function: linear; + border-color: var(--theme-ix-border); + border-radius: 50%; + border-style: solid; + border-width: 2px; + height: $size; + width: $size; + } + .spinner__bowtie { + animation: rotateHalf 1s infinite; + border-bottom-color: var(--theme-surface-on); + border-top-color: var(--theme-surface-on); + } + .spinner__half { + border-left-color: var(--theme-surface-on); + border-top-color: var(--theme-surface-on); + } + .spinner__shadow { + animation: rotateZ 0.75s infinite linear; + border-color: var(--theme-surface-on); + -webkit-mask: $mask; + mask: $mask; + -webkit-mask-composite: source-out; + mask-composite: subtract; + } +} + +@each $axis in $axes { + @keyframes rotate#{$axis} { + from { + transform: rotate#{$axis}#{'(0deg)'}; + } + to { + transform: rotate#{$axis}#{'(360deg)'}; + } + } +} + +@keyframes rotateHalf { + to { + transform: rotate(0.5turn); + } +} diff --git a/webui/react/src/components/kit/Icon/Spin.tsx b/webui/react/src/components/kit/Icon/Spin.tsx new file mode 100644 index 00000000000..615f8af5042 --- /dev/null +++ b/webui/react/src/components/kit/Icon/Spin.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import css from './Spin.module.scss'; + +interface Props { + type: 'bowtie' | 'half' | 'shadow'; +} + +const Spin: React.FC = ({ type }) => { + const classnames = [css.spinner, css[`spinner__${type}`]]; + return ( +
+
+
+ ); +}; + +export const SpinBowtie: React.FC = () => ; +export const SpinHalf: React.FC = () => ; +export const SpinShadow: React.FC = () => ; + +export default Spin; diff --git a/webui/react/src/components/kit/InlineForm.tsx b/webui/react/src/components/kit/InlineForm.tsx index 76d78b70ff2..8418b095655 100644 --- a/webui/react/src/components/kit/InlineForm.tsx +++ b/webui/react/src/components/kit/InlineForm.tsx @@ -154,7 +154,7 @@ function InlineForm({ />
+ {/* this is an invisible link used to programatically download the image file */}
- + ); }; diff --git a/webui/react/src/components/kit/useConfirm.tsx b/webui/react/src/components/kit/useConfirm.tsx index 208df860ebc..c2f08480a84 100644 --- a/webui/react/src/components/kit/useConfirm.tsx +++ b/webui/react/src/components/kit/useConfirm.tsx @@ -36,7 +36,7 @@ const ConfirmModal = ({ cancel cancelText={cancelText} danger={danger} - icon="warning-large" + icon="warning" size={size} submit={{ handleError: onError, diff --git a/webui/react/src/hooks/useModal/Checkpoint/useModalCheckpointDelete.tsx b/webui/react/src/hooks/useModal/Checkpoint/useModalCheckpointDelete.tsx deleted file mode 100644 index dfa741109a7..00000000000 --- a/webui/react/src/hooks/useModal/Checkpoint/useModalCheckpointDelete.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ExclamationCircleOutlined } from '@ant-design/icons'; -import { ModalFuncProps } from 'antd'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; - -import useModal, { ModalHooks as Hooks, ModalCloseReason } from 'hooks/useModal/useModal'; -import { detApi } from 'services/apiConfig'; -import { readStream } from 'services/utils'; -import { pluralizer } from 'utils/string'; - -interface OpenProps { - checkpoints: string | string[]; - initialModalProps?: ModalFuncProps; -} - -export interface Props { - onClose?: (reason?: ModalCloseReason) => void; -} - -interface ModalHooks extends Omit { - modalOpen: (openProps?: OpenProps) => void; -} - -const useModalCheckpointDelete = ({ onClose }: Props): ModalHooks => { - const { modalOpen: openOrUpdate, modalRef, ...modalHook } = useModal(); - const [checkpoints, setCheckpoints] = useState([]); - - const numCheckpoints = useMemo(() => { - if (Array.isArray(checkpoints)) return checkpoints.length; - return 1; - }, [checkpoints]); - - const handleCancel = useCallback(() => onClose?.(ModalCloseReason.Cancel), [onClose]); - - const handleDelete = useCallback(() => { - readStream( - detApi.Checkpoint.deleteCheckpoints({ - checkpointUuids: Array.isArray(checkpoints) ? checkpoints : [checkpoints], - }), - ); - onClose?.(ModalCloseReason.Ok); - }, [checkpoints, onClose]); - - const modalProps: ModalFuncProps = useMemo(() => { - const content = `Are you sure you want to request deletion for -${numCheckpoints} ${pluralizer(numCheckpoints, 'checkpoint')}? -This action may complete or fail without further notification.`; - - return { - content, - icon: , - okButtonProps: { danger: true }, - okText: 'Request Delete', - onCancel: handleCancel, - onOk: handleDelete, - title: 'Confirm Checkpoint Deletion', - width: 450, - }; - }, [handleCancel, handleDelete, numCheckpoints]); - - const modalOpen = useCallback( - ({ checkpoints, initialModalProps }: OpenProps = { checkpoints: [] }) => { - setCheckpoints(checkpoints); - openOrUpdate({ ...modalProps, ...initialModalProps }); - }, - [modalProps, openOrUpdate], - ); - - /** - * When modal props changes are detected, such as modal content - * title, and buttons, update the modal. - */ - useEffect(() => { - if (modalRef.current) openOrUpdate(modalProps); - }, [modalProps, modalRef, openOrUpdate]); - - return { modalOpen, modalRef, ...modalHook }; -}; - -export default useModalCheckpointDelete; diff --git a/webui/react/src/hooks/useModal/HyperparameterSearch/useModalHyperparameterSearch.tsx b/webui/react/src/hooks/useModal/HyperparameterSearch/useModalHyperparameterSearch.tsx index edbf9545b27..c11fbfaca4b 100644 --- a/webui/react/src/hooks/useModal/HyperparameterSearch/useModalHyperparameterSearch.tsx +++ b/webui/react/src/hooks/useModal/HyperparameterSearch/useModalHyperparameterSearch.tsx @@ -1,4 +1,3 @@ -import { InfoCircleOutlined } from '@ant-design/icons'; import { Select as AntdSelect, ModalFuncProps, Radio, Space, Typography } from 'antd'; import { RefSelectProps } from 'antd/lib/select'; import yaml from 'js-yaml'; @@ -12,7 +11,6 @@ import Input from 'components/kit/Input'; import InputNumber from 'components/kit/InputNumber'; import Message from 'components/kit/Message'; import Select, { Option, SelectValue } from 'components/kit/Select'; -import Tooltip from 'components/kit/Tooltip'; import { Loadable } from 'components/kit/utils/loadable'; import Link from 'components/Link'; import useModal, { ModalHooks as Hooks, ModalCloseReason } from 'hooks/useModal/useModal'; @@ -512,9 +510,11 @@ const useModalHyperparameterSearch = ({ label={
Early stopping mode - - - +
} name="mode" @@ -553,9 +553,7 @@ const useModalHyperparameterSearch = ({ label={
Max concurrent trials - - - +
} name="max_concurrent_trials" diff --git a/webui/react/src/pages/DesignKit.tsx b/webui/react/src/pages/DesignKit.tsx index fedf56d1fb2..8788809e6c3 100644 --- a/webui/react/src/pages/DesignKit.tsx +++ b/webui/react/src/pages/DesignKit.tsx @@ -1,4 +1,3 @@ -import { PoweroffOutlined } from '@ant-design/icons'; import { Card as AntDCard, Space } from 'antd'; import { SelectValue } from 'antd/es/select'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; @@ -58,7 +57,6 @@ import { CheckpointsDict } from 'pages/TrialDetails/TrialDetailsMetrics'; import { serverAddress } from 'routes/utils'; import { V1LogLevel } from 'services/api-ts-sdk'; import { mapV1LogsResponse } from 'services/decoder'; -import { BrandingType } from 'stores/determinedInfo'; import { Background, Brand, @@ -281,27 +279,20 @@ const ButtonsSection: React.FC = () => {
With icon - With SVG Icon + With Icon - + - With font icon - - - - - As Dropdown trigger with icon + As Dropdown trigger with Icon - + - - @@ -2439,10 +2430,19 @@ const IconsSection: React.FC = () => { ))} -

All icons

+

Icon colors

+ + + + +

All icons

+ |} wrap> {IconNameArray.map((name) => ( - + + +

{name}

+
))}
@@ -3132,7 +3132,7 @@ const DesignKit: React.FC = () => {
-