Feat(Theme): Support dark mode
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
.keyframes(@animationName, @content) {
|
||||
animation-name: @animationName;
|
||||
@-webkit-keyframes @animationName {
|
||||
@content();
|
||||
}
|
||||
@-moz-keyframes @animationName {
|
||||
@content();
|
||||
}
|
||||
@-o-keyframes @animationName {
|
||||
@content();
|
||||
}
|
||||
@keyframes @animationName {
|
||||
@content();
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
height: 0;
|
||||
|
||||
:global .monaco-editor-light {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--border);
|
||||
|
||||
.jsx-tag-angle-bracket {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
.jsx-text {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jsx-tag-name {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
.jsx-tag-attribute-key {
|
||||
color: #f00;
|
||||
}
|
||||
}
|
||||
|
||||
:global .monaco-editor-vs-dark {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--border);
|
||||
|
||||
.jsx-tag-angle-bracket {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.jsx-text {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.jsx-tag-name {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.jsx-tag-attribute-key {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
}
|
||||
|
||||
.playgroundCodeEditorLoading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 4px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border-top: 2px #666 solid;
|
||||
border-right: 2px #ddd solid;
|
||||
border-bottom: 2px #ddd solid;
|
||||
border-left: 2px #ddd solid;
|
||||
animation: .6s linear infinite;
|
||||
|
||||
.keyframes(fUHD7o, {
|
||||
0% {
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
53
src/components/Playground/CodeEditor/Editor/index.style.ts
Normal file
53
src/components/Playground/CodeEditor/Editor/index.style.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { createStyles, keyframes } from 'antd-style'
|
||||
|
||||
const rotate = keyframes`
|
||||
0% {
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
`
|
||||
|
||||
export default createStyles(() => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
height: 0,
|
||||
|
||||
'.monaco-editor-light': {
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'var(--border)',
|
||||
'.jsx-tag-angle-bracket': { color: '#800000' },
|
||||
'.jsx-text': { color: '#000' },
|
||||
'.jsx-tag-name': { color: '#800000' },
|
||||
'.jsx-tag-attribute-key': { color: '#f00' }
|
||||
},
|
||||
|
||||
'.monaco-editor-dark': {
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'var(--border)',
|
||||
'.jsx-tag-angle-bracket': { color: '#808080' },
|
||||
'.jsx-text': { color: '#d4d4d4' },
|
||||
'.jsx-tag-name': { color: '#569cd6' },
|
||||
'.jsx-tag-attribute-key': { color: '#9cdcfe' }
|
||||
}
|
||||
},
|
||||
|
||||
loading: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
margin: 4,
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: '50%',
|
||||
borderTop: '2px #666 solid',
|
||||
borderRight: '2px #ddd solid',
|
||||
borderBottom: '2px #ddd solid',
|
||||
borderLeft: '2px #ddd solid',
|
||||
animation: `${rotate} .6s linear infinite`
|
||||
}
|
||||
}))
|
||||
@@ -1,8 +1,8 @@
|
||||
import { editor, Selection } from 'monaco-editor'
|
||||
import MonacoEditor, { Monaco } from '@monaco-editor/react'
|
||||
import styles from '@/components/Playground/CodeEditor/Editor/index.module.less'
|
||||
import useStyles from '@/components/Playground/CodeEditor/Editor/index.style'
|
||||
import '@/components/Playground/CodeEditor/Editor/loader'
|
||||
import { IEditorOptions, IFiles, ITheme, ITsconfig } from '@/components/Playground/shared'
|
||||
import { IEditorOptions, IFiles, ITsconfig } from '@/components/Playground/shared'
|
||||
import { fileNameToLanguage, tsconfigJsonDiagnosticsOptions } from '@/components/Playground/files'
|
||||
import { useEditor, useTypesProgress } from '@/components/Playground/CodeEditor/Editor/hooks'
|
||||
import { MonacoEditorConfig } from '@/components/Playground/CodeEditor/Editor/monacoConfig'
|
||||
@@ -13,28 +13,29 @@ export interface ExtraLib {
|
||||
}
|
||||
|
||||
interface EditorProps {
|
||||
isDarkMode?: boolean
|
||||
tsconfig?: ITsconfig
|
||||
files?: IFiles
|
||||
selectedFileName?: string
|
||||
readonly?: boolean
|
||||
onChange?: (code: string | undefined) => void
|
||||
options?: IEditorOptions
|
||||
theme?: ITheme
|
||||
onJumpFile?: (fileName: string) => void
|
||||
extraLibs?: ExtraLib[]
|
||||
}
|
||||
|
||||
const Editor = ({
|
||||
isDarkMode,
|
||||
tsconfig,
|
||||
files = {},
|
||||
selectedFileName = '',
|
||||
readonly,
|
||||
theme,
|
||||
onChange,
|
||||
options,
|
||||
onJumpFile,
|
||||
extraLibs = []
|
||||
}: EditorProps) => {
|
||||
const { styles } = useStyles()
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
||||
const monacoRef = useRef<Monaco>()
|
||||
const { doOpenEditor, loadJsxSyntaxHighlight, autoLoadExtraLib } = useEditor()
|
||||
@@ -114,9 +115,9 @@ const Editor = ({
|
||||
<>
|
||||
<div className={styles.root}>
|
||||
<MonacoEditor
|
||||
theme={theme}
|
||||
theme={isDarkMode ? 'vs-dark' : 'light'}
|
||||
path={file.name}
|
||||
className={`monaco-editor-${theme ?? 'light'}`}
|
||||
className={`monaco-editor-${isDarkMode ? 'dark' : 'light'}`}
|
||||
language={file.language}
|
||||
value={file.value}
|
||||
onChange={onChange}
|
||||
@@ -129,7 +130,7 @@ const Editor = ({
|
||||
readOnly: readonly
|
||||
}}
|
||||
/>
|
||||
{total > 0 && !finished && <div className={styles.playgroundCodeEditorLoading} />}
|
||||
{total > 0 && !finished && <div className={styles.loading} />}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Dispatch, SetStateAction, KeyboardEvent, ChangeEvent, MouseEvent } from 'react'
|
||||
import styles from '@/components/Playground/CodeEditor/FileSelector/item.module.less'
|
||||
import useStyles from '@/components/Playground/CodeEditor/FileSelector/item.style'
|
||||
|
||||
interface ItemProps {
|
||||
className?: string
|
||||
@@ -30,6 +30,7 @@ const Item = ({
|
||||
onValidate,
|
||||
...prop
|
||||
}: ItemProps) => {
|
||||
const { styles, cx } = useStyles()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [fileName, setFileName] = useState(value)
|
||||
const [isCreating, setIsCreating] = useState(prop.creating)
|
||||
@@ -110,7 +111,7 @@ const Item = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.root}${active ? ` ${styles.active}` : ''}${className ? ` ${className}` : ''}`}
|
||||
className={cx(styles.root, active ? styles.active : '', className)}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
{isCreating ? (
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
.root{
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
height: 40px;
|
||||
|
||||
.multiple {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
|
||||
.tabContent {
|
||||
height: 40px;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
margin-left: 10px;
|
||||
|
||||
.tabItemAdd {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.tabsMarginRight {
|
||||
height: 100%;
|
||||
|
||||
> * {
|
||||
height: 100%;
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sticky {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: flex-end;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(() => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flex: '0 0 auto',
|
||||
height: 40
|
||||
},
|
||||
|
||||
multiple: {
|
||||
flex: 1,
|
||||
width: 0
|
||||
},
|
||||
|
||||
tabContent: {
|
||||
height: 40,
|
||||
alignItems: 'flex-end',
|
||||
gap: 2,
|
||||
marginLeft: 10
|
||||
},
|
||||
|
||||
tabItemAdd: {
|
||||
padding: '0 12px'
|
||||
},
|
||||
|
||||
tabsMarginRight: {
|
||||
height: '100%',
|
||||
|
||||
'> *': {
|
||||
height: '100%',
|
||||
width: 10
|
||||
}
|
||||
},
|
||||
|
||||
sticky: {
|
||||
display: 'flex',
|
||||
flex: '0 0 auto',
|
||||
alignItems: 'flex-end',
|
||||
marginRight: 10
|
||||
}
|
||||
}))
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from '@/components/Playground/CodeEditor/FileSelector/index.module.less'
|
||||
import useStyles from '@/components/Playground/CodeEditor/FileSelector/index.style'
|
||||
import HideScrollbar, { HideScrollbarElement } from '@/components/common/HideScrollbar'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { IFiles } from '@/components/Playground/shared'
|
||||
@@ -32,6 +32,7 @@ const FileSelector = ({
|
||||
onUpdateFileName,
|
||||
selectedFileName = ''
|
||||
}: FileSelectorProps) => {
|
||||
const { styles } = useStyles()
|
||||
const hideScrollbarRef = useRef<HideScrollbarElement>(null)
|
||||
const [tabs, setTabs] = useState<string[]>([])
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
height: 30px;
|
||||
padding: 0 20px;
|
||||
border: 1px solid #f0f0f0;
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 6px 6px 0 0;
|
||||
cursor: pointer;
|
||||
|
||||
.tabItemInput {
|
||||
position: relative;
|
||||
min-width: 40px;
|
||||
transform: translateY(1px);
|
||||
|
||||
.tabItemInputMask {
|
||||
display: inline-block;
|
||||
color: transparent;
|
||||
}
|
||||
input {
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.tabItemClose {
|
||||
transform: translateX(10px);
|
||||
|
||||
:hover {
|
||||
fill: #888;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 8px;
|
||||
fill: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: white;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(({ token }) => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flex: '0 0 auto',
|
||||
height: 30,
|
||||
padding: '0 20px',
|
||||
color: token.colorText,
|
||||
border: `1px solid ${token.colorBorder}`,
|
||||
backgroundColor: token.colorBgLayout,
|
||||
borderRadius: '6px 6px 0 0',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
|
||||
active: {
|
||||
backgroundColor: token.colorBgElevated,
|
||||
borderBottom: 'none'
|
||||
},
|
||||
|
||||
tabItemInput: {
|
||||
position: 'relative',
|
||||
minWidth: 40,
|
||||
transform: 'translateY(1px)',
|
||||
|
||||
input: {
|
||||
position: 'absolute',
|
||||
backgroundColor: 'transparent',
|
||||
width: '100%',
|
||||
color: token.colorText,
|
||||
fontSize: token.fontSizeSM
|
||||
}
|
||||
},
|
||||
|
||||
tabItemInputMask: {
|
||||
display: 'inline-block',
|
||||
color: 'transparent'
|
||||
},
|
||||
|
||||
tabItemClose: {
|
||||
transform: 'translateX(10px)',
|
||||
|
||||
svg: {
|
||||
height: token.sizeXS,
|
||||
fill: token.colorTextSecondary
|
||||
},
|
||||
|
||||
'>:hover': {
|
||||
fill: token.colorTextDescription
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -1,15 +0,0 @@
|
||||
.root {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.errorMessage {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #FF4D4FAA;
|
||||
padding: 5px 10px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
19
src/components/Playground/CodeEditor/index.style.ts
Normal file
19
src/components/Playground/CodeEditor/index.style.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(({ token }) => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
},
|
||||
|
||||
errorMessage: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
color: token.colorErrorText,
|
||||
backgroundColor: token.colorErrorBg,
|
||||
padding: '5px 10px',
|
||||
fontSize: token.fontSize
|
||||
}
|
||||
}))
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash'
|
||||
import styles from '@/components/Playground/CodeEditor/index.module.less'
|
||||
import useStyles from '@/components/Playground/CodeEditor/index.style'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { IEditorOptions, IFiles, ITheme, ITsconfig } from '@/components/Playground/shared'
|
||||
import { IEditorOptions, IFiles, ITsconfig } from '@/components/Playground/shared'
|
||||
import {
|
||||
fileNameToLanguage,
|
||||
getFileNameList,
|
||||
@@ -12,7 +12,7 @@ import FileSelector from '@/components/Playground/CodeEditor/FileSelector'
|
||||
import Editor, { ExtraLib } from '@/components/Playground/CodeEditor/Editor'
|
||||
|
||||
interface CodeEditorProps {
|
||||
theme?: ITheme
|
||||
isDarkMode?: boolean
|
||||
showFileSelector?: boolean
|
||||
tsconfig?: ITsconfig
|
||||
files: IFiles
|
||||
@@ -31,7 +31,7 @@ interface CodeEditorProps {
|
||||
}
|
||||
|
||||
const CodeEditor = ({
|
||||
theme,
|
||||
isDarkMode,
|
||||
showFileSelector = true,
|
||||
tsconfig,
|
||||
files,
|
||||
@@ -48,6 +48,7 @@ const CodeEditor = ({
|
||||
extraLibs,
|
||||
...props
|
||||
}: CodeEditorProps) => {
|
||||
const { styles } = useStyles()
|
||||
const filteredFilesName = getFileNameList(files).filter(
|
||||
(item) => ![IMPORT_MAP_FILE_NAME, TS_CONFIG_FILE_NAME].includes(item) && !files[item].hidden
|
||||
)
|
||||
@@ -139,8 +140,8 @@ const CodeEditor = ({
|
||||
/>
|
||||
)}
|
||||
<Editor
|
||||
isDarkMode={isDarkMode}
|
||||
tsconfig={tsconfig}
|
||||
theme={theme}
|
||||
selectedFileName={
|
||||
onSelectedFileChange ? propsSelectedFileName : selectedFileName
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ChangeEvent } from 'react'
|
||||
import styles from '@/components/Playground/Output/Preview/render.module.less'
|
||||
import { COLOR_FONT_MAIN } from '@/constants/common.constants'
|
||||
import useStyles from '@/components/Playground/Output/Preview/render.style'
|
||||
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
|
||||
@@ -41,6 +40,7 @@ const getIframeUrl = (iframeRaw: string) => {
|
||||
const iframeUrl = getIframeUrl(iframeRaw)
|
||||
|
||||
const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) => {
|
||||
const { styles, theme, cx } = useStyles()
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null)
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
const [selectedDevice, setSelectedDevice] = useState('Pixel 7')
|
||||
@@ -164,7 +164,7 @@ const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) =>
|
||||
iframeRef.current?.contentWindow?.postMessage(
|
||||
{
|
||||
type: 'SCALE',
|
||||
data: { zoom: zoom }
|
||||
data: { zoom }
|
||||
} as IMessage,
|
||||
'*'
|
||||
)
|
||||
@@ -180,12 +180,18 @@ const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) =>
|
||||
autoHideWaitingTime={1000}
|
||||
>
|
||||
<div className={styles.mobileModeContent} style={{ zoom }}>
|
||||
<div className={`${styles.device}${isRotate ? ` ${styles.rotate}` : ''}`}>
|
||||
<div className={cx(styles.device, isRotate ? styles.rotate : '')}>
|
||||
<div
|
||||
className={`${styles.deviceHeader}${isRotate ? ` ${styles.rotate}` : ''}`}
|
||||
className={cx(
|
||||
styles.deviceHeader,
|
||||
isRotate ? styles.rotatedDeviceHeader : ''
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={`${styles.deviceContent}${isRotate ? ` ${styles.rotate}` : ''}`}
|
||||
className={cx(
|
||||
styles.deviceContent,
|
||||
isRotate ? styles.rotatedDeviceContent : ''
|
||||
)}
|
||||
style={{
|
||||
width: isRotate
|
||||
? (devices.find((value) => value.name === selectedDevice)
|
||||
@@ -210,14 +216,17 @@ const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) =>
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.deviceFooter}${isRotate ? ` ${styles.rotate}` : ''}`}
|
||||
className={cx(
|
||||
styles.deviceFooter,
|
||||
isRotate ? styles.rotatedDeviceFooter : ''
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</HideScrollbar>
|
||||
|
||||
<div className={styles.switchDevice}>
|
||||
<IconOxygenMobile fill={COLOR_FONT_MAIN} />
|
||||
<IconOxygenMobile fill={theme.colorText} />
|
||||
<select value={selectedDevice} onChange={handleOnChangeDevice}>
|
||||
{devices.map((value) => (
|
||||
<option value={value.name}>{value.name}</option>
|
||||
@@ -225,14 +234,14 @@ const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) =>
|
||||
</select>
|
||||
<div title={'旋转屏幕'} onClick={handleOnRotateDevice}>
|
||||
{isRotate ? (
|
||||
<IconOxygenRotateRight fill={COLOR_FONT_MAIN} />
|
||||
<IconOxygenRotateRight fill={theme.colorText} />
|
||||
) : (
|
||||
<IconOxygenRotateLeft fill={COLOR_FONT_MAIN} />
|
||||
<IconOxygenRotateLeft fill={theme.colorText} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.switchZoom}>
|
||||
<IconOxygenZoom fill={COLOR_FONT_MAIN} />
|
||||
<IconOxygenZoom fill={theme.colorText} />
|
||||
<input
|
||||
type={'range'}
|
||||
min={0.5}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.root {
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 0;
|
||||
|
||||
.errorMessage {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #FF4D4FAA;
|
||||
padding: 5px 10px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
19
src/components/Playground/Output/Preview/index.style.ts
Normal file
19
src/components/Playground/Output/Preview/index.style.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(({ token }) => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
height: 0
|
||||
},
|
||||
|
||||
errorMessage: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
color: token.colorErrorText,
|
||||
backgroundColor: token.colorErrorBg,
|
||||
padding: '5px 10px',
|
||||
fontSize: token.fontSize
|
||||
}
|
||||
}))
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from '@/components/Playground/Output/Preview/index.module.less'
|
||||
import useStyles from '@/components/Playground/Output/Preview/index.style'
|
||||
import { IFiles, IImportMap } from '@/components/Playground/shared'
|
||||
import Compiler from '@/components/Playground/compiler'
|
||||
import Render from '@/components/Playground/Output/Preview/Render'
|
||||
@@ -22,6 +22,7 @@ const Preview = ({
|
||||
postExpansionCode = '',
|
||||
mobileMode = false
|
||||
}: PreviewProps) => {
|
||||
const { styles } = useStyles()
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
const [compiledCode, setCompiledCode] = useState('')
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
.renderRoot {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mobileModeRoot {
|
||||
background-color: rgba(204, 204, 204, 0.66);
|
||||
|
||||
.mobileModeContent {
|
||||
padding: 80px;
|
||||
}
|
||||
|
||||
.device {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #EEEFF2;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
border-radius: 40px;
|
||||
|
||||
&.rotate {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.deviceHeader {
|
||||
margin: 20px auto;
|
||||
width: 60px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #C8C9CC;
|
||||
|
||||
&.rotate {
|
||||
margin: auto 20px;
|
||||
width: 10px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.deviceContent {
|
||||
margin: 0 10px;
|
||||
background-color: white;
|
||||
|
||||
&.rotate {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.deviceFooter {
|
||||
margin: 20px auto;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #C8C9CC;
|
||||
|
||||
&.rotate {
|
||||
margin: auto 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switchDevice, .switchZoom {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.switchDevice {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.switchZoom {
|
||||
right: 10px;
|
||||
}
|
||||
84
src/components/Playground/Output/Preview/render.style.ts
Normal file
84
src/components/Playground/Output/Preview/render.style.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(({ token }) => ({
|
||||
renderRoot: {
|
||||
border: 'none',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
flex: 1
|
||||
},
|
||||
|
||||
mobileModeRoot: {
|
||||
backgroundColor: token.colorBgLayout
|
||||
},
|
||||
|
||||
mobileModeContent: {
|
||||
padding: 80
|
||||
},
|
||||
|
||||
device: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: token.colorBgElevated,
|
||||
width: 'fit-content',
|
||||
margin: '0 auto',
|
||||
borderRadius: 40
|
||||
},
|
||||
|
||||
rotate: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
deviceHeader: {
|
||||
margin: '20px auto',
|
||||
width: 60,
|
||||
height: 10,
|
||||
borderRadius: 5,
|
||||
backgroundColor: token.colorBgMask
|
||||
},
|
||||
|
||||
rotatedDeviceHeader: {
|
||||
margin: 'auto 20px',
|
||||
width: 10,
|
||||
height: 60
|
||||
},
|
||||
|
||||
deviceContent: {
|
||||
margin: '0 10px',
|
||||
backgroundColor: token.colorBgLayout
|
||||
},
|
||||
|
||||
rotatedDeviceContent: {
|
||||
margin: '10px 0'
|
||||
},
|
||||
|
||||
deviceFooter: {
|
||||
margin: '20px auto',
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: token.colorBgMask
|
||||
},
|
||||
|
||||
rotatedDeviceFooter: {
|
||||
margin: 'auto 20px'
|
||||
},
|
||||
|
||||
switchDevice: {
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: 10,
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
},
|
||||
|
||||
switchZoom: {
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
}
|
||||
}))
|
||||
@@ -1,13 +0,0 @@
|
||||
.root {
|
||||
position: relative;
|
||||
|
||||
.errorMessage {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #FF4D4FAA;
|
||||
padding: 5px 10px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
17
src/components/Playground/Output/Transform/index.style.ts
Normal file
17
src/components/Playground/Output/Transform/index.style.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(({ token }) => ({
|
||||
root: {
|
||||
position: 'relative'
|
||||
},
|
||||
|
||||
errorMessage: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
color: token.colorErrorText,
|
||||
backgroundColor: token.colorErrorBg,
|
||||
padding: '5px 10px',
|
||||
fontSize: token.fontSize
|
||||
}
|
||||
}))
|
||||
@@ -1,6 +1,6 @@
|
||||
import MonacoEditor from '@monaco-editor/react'
|
||||
import { Loader } from 'esbuild-wasm'
|
||||
import styles from '@/components/Playground/Output/Transform/index.module.less'
|
||||
import useStyles from '@/components/Playground/Output/Transform/index.style'
|
||||
import { IFile, ITheme } from '@/components/Playground/shared'
|
||||
import { cssToJsFromFile, jsonToJsFromFile } from '@/components/Playground/files'
|
||||
import Compiler from '@/components/Playground/compiler'
|
||||
@@ -12,6 +12,7 @@ interface OutputProps {
|
||||
}
|
||||
|
||||
const Transform = ({ file, theme }: OutputProps) => {
|
||||
const { styles } = useStyles()
|
||||
const [compiledCode, setCompiledCode] = useState('')
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
.root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
> * {
|
||||
width: 0 !important;
|
||||
}
|
||||
}
|
||||
12
src/components/Playground/index.style.ts
Normal file
12
src/components/Playground/index.style.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createStyles } from 'antd-style'
|
||||
|
||||
export default createStyles(() => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
|
||||
'> *': {
|
||||
width: '0 !important'
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from '@/components/Playground/index.module.less'
|
||||
import useStyles from '@/components/Playground/index.style'
|
||||
import { IFiles, IImportMap, ITsconfig } from '@/components/Playground/shared'
|
||||
import {
|
||||
ENTRY_FILE_NAME,
|
||||
@@ -11,6 +11,7 @@ import CodeEditor from '@/components/Playground/CodeEditor'
|
||||
import Output from '@/components/Playground/Output'
|
||||
|
||||
interface PlaygroundProps {
|
||||
isDarkMode?: boolean
|
||||
initFiles: IFiles
|
||||
initImportMapRaw: string
|
||||
initTsconfigRaw: string
|
||||
@@ -18,11 +19,13 @@ interface PlaygroundProps {
|
||||
}
|
||||
|
||||
const Playground = ({
|
||||
isDarkMode,
|
||||
initFiles,
|
||||
initImportMapRaw,
|
||||
initTsconfigRaw,
|
||||
entryPoint = ENTRY_FILE_NAME
|
||||
}: PlaygroundProps) => {
|
||||
const { styles } = useStyles()
|
||||
const [files, setFiles] = useState(initFiles)
|
||||
const [selectedFileName, setSelectedFileName] = useState(MAIN_FILE_NAME)
|
||||
const [importMapRaw, setImportMapRaw] = useState<string>(initImportMapRaw)
|
||||
@@ -79,6 +82,7 @@ const Playground = ({
|
||||
return (
|
||||
<FlexBox className={styles.root} direction={'horizontal'}>
|
||||
<CodeEditor
|
||||
isDarkMode={isDarkMode}
|
||||
tsconfig={tsconfig}
|
||||
files={{
|
||||
...files,
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import styles from '@/assets/css/components/common/card.module.less'
|
||||
import useStyles from '@/assets/css/components/common/card.style'
|
||||
|
||||
type CardProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
|
||||
|
||||
const Card = forwardRef<HTMLDivElement, CardProps>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
className={`${styles.cardBox}${className ? ` ${className}` : ''}`}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
return <div className={cx(styles.cardBox, className)} {...props} ref={ref} />
|
||||
})
|
||||
|
||||
export default Card
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import styles from '@/assets/css/components/common/fit-center.module.less'
|
||||
import useStyles from '@/assets/css/components/common/fit-center.style'
|
||||
|
||||
interface FitCenterProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
vertical?: boolean
|
||||
}
|
||||
|
||||
const FitCenter = ({ className, vertical, ...props }: FitCenterProps) => {
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.fitCenter}${className ? ` ${className}` : ''}${
|
||||
className={cx(
|
||||
styles.fitCenter,
|
||||
className,
|
||||
vertical ? ' flex-vertical' : ' flex-horizontal'
|
||||
}`}
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import styles from '@/assets/css/components/common/fit-fullscreen.module.less'
|
||||
import useStyles from '@/assets/css/components/common/fit-fullscreen.style'
|
||||
|
||||
interface FitFullscreenProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
@@ -9,9 +9,11 @@ interface FitFullscreenProps
|
||||
|
||||
const FitFullscreen = forwardRef<HTMLDivElement, FitFullscreenProps>(
|
||||
({ zIndex, backgroundColor, className, style, ...props }, ref) => {
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.fitFullscreen}${className ? ` ${className}` : ''}`}
|
||||
className={cx(styles.fitFullscreen, className)}
|
||||
style={{
|
||||
zIndex,
|
||||
backgroundColor,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import styles from '@/assets/css/components/common/flex-box.module.less'
|
||||
import useStyles from '@/assets/css/components/common/flex-box.style'
|
||||
|
||||
interface FlexBoxProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
@@ -8,11 +8,15 @@ interface FlexBoxProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>,
|
||||
|
||||
const FlexBox = forwardRef<HTMLDivElement, FlexBoxProps>(
|
||||
({ className, direction, gap, style, ...props }, ref) => {
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.flexBox} ${
|
||||
className={cx(
|
||||
styles.flexBox,
|
||||
className,
|
||||
direction === 'horizontal' ? 'flex-horizontal' : 'flex-vertical'
|
||||
}${className ? ` ${className}` : ''}`}
|
||||
)}
|
||||
style={{ gap, ...style }}
|
||||
{...props}
|
||||
ref={ref}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/common/fullscreen-loading-mask.module.less'
|
||||
import { COLOR_FONT_MAIN } from '@/constants/common.constants'
|
||||
import useStyles from '@/assets/css/components/common/fullscreen-loading-mask.style'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
|
||||
const FullscreenLoadingMask = () => {
|
||||
const { styles, theme } = useStyles()
|
||||
|
||||
const loadingIcon = (
|
||||
<>
|
||||
<Icon
|
||||
component={IconOxygenLoading}
|
||||
style={{ fontSize: 24, color: COLOR_FONT_MAIN }}
|
||||
style={{ fontSize: 24, color: theme.colorText }}
|
||||
spin
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
HTMLAttributes,
|
||||
UIEvent
|
||||
} from 'react'
|
||||
import styles from '@/assets/css/components/common/hide-scrollbar.module.less'
|
||||
import useStyles from '@/assets/css/components/common/hide-scrollbar.style'
|
||||
|
||||
interface HideScrollbarProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
@@ -82,6 +82,8 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
useImperativeHandle<HideScrollbarElement, HideScrollbarElement>(ref, () => {
|
||||
return {
|
||||
scrollTo(x, y, smooth?: boolean) {
|
||||
@@ -538,7 +540,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
>
|
||||
<div
|
||||
ref={rootRef}
|
||||
className={`${styles.hideScrollbarSelection}${className ? ` ${className}` : ''}`}
|
||||
className={cx(styles.hideScrollbarSelection, className)}
|
||||
tabIndex={0}
|
||||
style={{
|
||||
width: `calc(${maskRef.current?.clientWidth}px + ${verticalScrollbarWidth}px)`,
|
||||
@@ -566,9 +568,11 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
{isShowVerticalScrollbar &&
|
||||
(!isHiddenVerticalScrollbarWhenFull || verticalScrollbarLength < 100) && (
|
||||
<div
|
||||
className={`${styles.scrollbar} ${styles.verticalScrollbar}${
|
||||
className={cx(
|
||||
styles.scrollbar,
|
||||
styles.verticalScrollbar,
|
||||
isVerticalScrollbarAutoHide ? ` ${styles.hide}` : ''
|
||||
}`}
|
||||
)}
|
||||
style={{
|
||||
height: maskRef.current
|
||||
? maskRef.current?.clientHeight - 1
|
||||
@@ -582,9 +586,12 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
padding: `${scrollbarAsidePadding}px ${scrollbarEdgePadding}px`
|
||||
}}
|
||||
>
|
||||
<div className={styles.box} style={{ width: scrollbarWidth }}>
|
||||
<div
|
||||
className={styles.scrollbarBox}
|
||||
style={{ width: scrollbarWidth }}
|
||||
>
|
||||
<div
|
||||
className={styles.block}
|
||||
className={styles.scrollbarBoxBlock}
|
||||
style={{
|
||||
height: `${verticalScrollbarLength}%`,
|
||||
top: `clamp(0px, ${verticalScrollbarPosition}%, ${
|
||||
@@ -609,9 +616,11 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
(!isHiddenHorizontalScrollbarWhenFull ||
|
||||
horizontalScrollbarLength < 100) && (
|
||||
<div
|
||||
className={`${styles.scrollbar} ${styles.horizontalScrollbar}${
|
||||
className={cx(
|
||||
styles.scrollbar,
|
||||
styles.horizontalScrollbar,
|
||||
isHorizontalScrollbarAutoHide ? ` ${styles.hide}` : ''
|
||||
}`}
|
||||
)}
|
||||
style={{
|
||||
width: maskRef.current
|
||||
? maskRef.current?.clientWidth - 1
|
||||
@@ -625,9 +634,12 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
||||
padding: `${scrollbarEdgePadding}px ${scrollbarAsidePadding}px`
|
||||
}}
|
||||
>
|
||||
<div className={styles.box} style={{ height: scrollbarWidth }}>
|
||||
<div
|
||||
className={styles.scrollbarBox}
|
||||
style={{ height: scrollbarWidth }}
|
||||
>
|
||||
<div
|
||||
className={styles.block}
|
||||
className={styles.scrollbarBoxBlock}
|
||||
style={{
|
||||
width: `${horizontalScrollbarLength}%`,
|
||||
left: `clamp(0px, ${horizontalScrollbarPosition}%, ${
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
import styles from '@/assets/css/components/common/indicator.module.less'
|
||||
|
||||
interface IndicatorProps {
|
||||
total: number
|
||||
current: number
|
||||
onSwitch?: (index: number) => void
|
||||
}
|
||||
|
||||
const Indicator = ({ total, current, onSwitch }: IndicatorProps) => {
|
||||
const handleClick = (index: number) => {
|
||||
return () => {
|
||||
onSwitch?.(index)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul className={`${styles.dotList} flex-vertical`}>
|
||||
{_.range(0, total).map((_value, index) => {
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className={`${styles.item} center-box${index === current ? ` ${styles.active}` : ''}`}
|
||||
onClick={handleClick(index)}
|
||||
>
|
||||
<div className={styles.dot} />
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Indicator
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/common/loading-mask.module.less'
|
||||
import { COLOR_FONT_MAIN } from '@/constants/common.constants'
|
||||
import useStyles from '@/assets/css/components/common/loading-mask.style'
|
||||
|
||||
interface LoadingMaskProps extends PropsWithChildren {
|
||||
hidden?: boolean
|
||||
@@ -9,11 +8,13 @@ interface LoadingMaskProps extends PropsWithChildren {
|
||||
}
|
||||
|
||||
const LoadingMask = (props: LoadingMaskProps) => {
|
||||
const { styles, theme } = useStyles()
|
||||
|
||||
const loadingIcon = (
|
||||
<>
|
||||
<Icon
|
||||
component={IconOxygenLoading}
|
||||
style={{ fontSize: 24, color: COLOR_FONT_MAIN }}
|
||||
style={{ fontSize: 24, color: theme.colorText }}
|
||||
spin
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import { COLOR_ERROR } from '@/constants/common.constants'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/footer.style'
|
||||
import { SidebarContext } from '@/components/common/Sidebar/index'
|
||||
import { getRedirectUrl } from '@/util/route'
|
||||
import { getAvatar, getLoginStatus, getNickname, removeToken } from '@/util/auth'
|
||||
import { navigateToLogin, navigateToUser } from '@/util/navigation'
|
||||
import { r_auth_logout } from '@/services/auth'
|
||||
|
||||
const Footer = () => {
|
||||
const { styles, theme, cx } = useStyles()
|
||||
const { isCollapse } = useContext(SidebarContext)
|
||||
const matches = useMatches()
|
||||
const lastMatch = matches.reduce((_, second) => second)
|
||||
const location = useLocation()
|
||||
@@ -33,7 +35,7 @@ const Footer = () => {
|
||||
removeToken()
|
||||
notification.info({
|
||||
message: '已退出登录',
|
||||
icon: <Icon component={IconOxygenExit} style={{ color: COLOR_ERROR }} />
|
||||
icon: <Icon component={IconOxygenExit} style={{ color: theme.colorErrorText }} />
|
||||
})
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
@@ -58,7 +60,7 @@ const Footer = () => {
|
||||
return (
|
||||
<div className={styles.footer}>
|
||||
<span
|
||||
className={styles.iconUser}
|
||||
className={styles.icon}
|
||||
onClick={handleClickAvatar}
|
||||
title={getLoginStatus() ? '个人中心' : '登录'}
|
||||
>
|
||||
@@ -74,17 +76,29 @@ const Footer = () => {
|
||||
登录
|
||||
</NavLink>
|
||||
</span>
|
||||
<span hidden={!getLoginStatus()} className={styles.text} title={nickname}>
|
||||
<span
|
||||
hidden={!getLoginStatus()}
|
||||
className={cx(styles.text, isCollapse ? styles.collapsedExit : '')}
|
||||
title={nickname}
|
||||
>
|
||||
{nickname}
|
||||
</span>
|
||||
<div
|
||||
hidden={!getLoginStatus()}
|
||||
className={`${styles.submenuExit}${!getLoginStatus() ? ` ${styles.hide}` : ''}`}
|
||||
className={cx(
|
||||
isCollapse ? styles.collapsedExit : '',
|
||||
!getLoginStatus() ? styles.hide : ''
|
||||
)}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<div
|
||||
className={cx(
|
||||
styles.exitContent,
|
||||
isCollapse ? styles.collapsedExitContent : ''
|
||||
)}
|
||||
>
|
||||
<span
|
||||
hidden={!getLoginStatus()}
|
||||
className={styles.iconExit}
|
||||
className={cx(styles.exitIcon, isCollapse ? styles.collapsedExitIcon : '')}
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<Icon
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { ReactNode, MouseEvent } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import Submenu from '@/components/common/Sidebar/Submenu'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/item.style'
|
||||
import { SidebarContext } from '@/components/common/Sidebar/index'
|
||||
import Submenu, { SubmenuContext } from '@/components/common/Sidebar/Submenu'
|
||||
|
||||
export const ItemContext = createContext({ isHover: false })
|
||||
|
||||
type ItemProps = {
|
||||
icon?: IconComponent | string
|
||||
@@ -13,6 +16,10 @@ type ItemProps = {
|
||||
}
|
||||
|
||||
const Item = (props: ItemProps) => {
|
||||
const { styles, cx } = useStyles()
|
||||
const { isCollapse } = useContext(SidebarContext)
|
||||
const { isInSubmenu } = useContext(SubmenuContext)
|
||||
const [isHover, setIsHover] = useState(false)
|
||||
const [submenuTop, setSubmenuTop] = useState(Number.MAX_VALUE)
|
||||
const [submenuLeft, setSubmenuLeft] = useState(Number.MAX_VALUE)
|
||||
|
||||
@@ -34,34 +41,52 @@ const Item = (props: ItemProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
<div className={styles.menuBt} onMouseEnter={showSubmenu}>
|
||||
<NavLink
|
||||
end={props.end}
|
||||
to={props.path}
|
||||
className={({ isActive, isPending }) =>
|
||||
isPending ? 'pending' : isActive ? `${styles.active}` : ''
|
||||
}
|
||||
>
|
||||
{props.icon && (
|
||||
<div className={styles.iconBox}>
|
||||
{typeof props.icon === 'string' ? (
|
||||
<img src={`data:image/svg+xml;base64,${props.icon}`} alt={'icon'} />
|
||||
) : (
|
||||
<Icon component={props.icon} />
|
||||
<ItemContext.Provider value={{ isHover }}>
|
||||
<li
|
||||
className={cx(styles.item, isInSubmenu ? styles.submenuItem : '')}
|
||||
onMouseOver={() => setIsHover(true)}
|
||||
onMouseLeave={() => setIsHover(false)}
|
||||
>
|
||||
<div className={cx(styles.menuBt, 'dnd-delete-mask')} onMouseEnter={showSubmenu}>
|
||||
<NavLink
|
||||
end={props.end}
|
||||
to={props.path}
|
||||
className={({ isActive, isPending }) =>
|
||||
isPending ? 'pending' : isActive ? `${styles.active}` : ''
|
||||
}
|
||||
>
|
||||
{props.icon && (
|
||||
<div className={styles.icon}>
|
||||
{typeof props.icon === 'string' ? (
|
||||
<img
|
||||
src={`data:image/svg+xml;base64,${props.icon}`}
|
||||
alt={'icon'}
|
||||
/>
|
||||
) : (
|
||||
<Icon component={props.icon} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<span
|
||||
className={cx(
|
||||
styles.text,
|
||||
isCollapse && !isInSubmenu ? styles.collapsedText : ''
|
||||
)}
|
||||
>
|
||||
{props.text}
|
||||
</span>
|
||||
<div className={isCollapse ? styles.collapsedExtend : ''}>
|
||||
{props.extend}
|
||||
</div>
|
||||
)}
|
||||
<span className={styles.text}>{props.text}</span>
|
||||
<div className={styles.extend}>{props.extend}</div>
|
||||
</NavLink>
|
||||
</div>
|
||||
{props.children && (
|
||||
<Submenu submenuTop={submenuTop} submenuLeft={submenuLeft}>
|
||||
{props.children}
|
||||
</Submenu>
|
||||
)}
|
||||
</li>
|
||||
</NavLink>
|
||||
</div>
|
||||
{props.children && (
|
||||
<Submenu submenuTop={submenuTop} submenuLeft={submenuLeft}>
|
||||
{props.children}
|
||||
</Submenu>
|
||||
)}
|
||||
</li>
|
||||
</ItemContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/scroll.style'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
|
||||
const Scroll = (props: PropsWithChildren) => {
|
||||
const { styles } = useStyles()
|
||||
|
||||
return (
|
||||
<div className={styles.scroll}>
|
||||
<HideScrollbar
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/separate'
|
||||
|
||||
const Separate = ({
|
||||
className,
|
||||
...props
|
||||
}: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => {
|
||||
return <div className={`${styles.separate}${className ? ` ${className}` : ''}`} {...props} />
|
||||
const { styles, cx } = useStyles()
|
||||
|
||||
return <div className={cx(styles.separate, className)} {...props} />
|
||||
}
|
||||
|
||||
export default Separate
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/submenu.style'
|
||||
import { ItemContext } from '@/components/common/Sidebar/Item'
|
||||
|
||||
export const SubmenuContext = createContext({ isInSubmenu: false })
|
||||
|
||||
interface SidebarSubmenuProps extends PropsWithChildren {
|
||||
submenuTop: number
|
||||
@@ -7,16 +10,21 @@ interface SidebarSubmenuProps extends PropsWithChildren {
|
||||
}
|
||||
|
||||
const Submenu = (props: SidebarSubmenuProps) => {
|
||||
const { styles, cx } = useStyles()
|
||||
const { isHover } = useContext(ItemContext)
|
||||
|
||||
return (
|
||||
<ul
|
||||
className={styles.submenu}
|
||||
style={{
|
||||
top: props.submenuTop,
|
||||
left: props.submenuLeft
|
||||
}}
|
||||
>
|
||||
<div className={styles.content}>{props.children}</div>
|
||||
</ul>
|
||||
<SubmenuContext.Provider value={{ isInSubmenu: true }}>
|
||||
<ul
|
||||
className={cx(styles.submenu, isHover ? styles.hoveredSubmenu : '')}
|
||||
style={{
|
||||
top: props.submenuTop,
|
||||
left: props.submenuLeft - 1
|
||||
}}
|
||||
>
|
||||
<div className={styles.content}>{props.children}</div>
|
||||
</ul>
|
||||
</SubmenuContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/common/sidebar.module.less'
|
||||
import useStyles from '@/assets/css/components/common/sidebar/index.style'
|
||||
import { getLocalStorage, setLocalStorage } from '@/util/browser'
|
||||
import Item from '@/components/common/Sidebar/Item'
|
||||
import ItemList from '@/components/common/Sidebar/ItemList'
|
||||
@@ -9,6 +9,8 @@ import Separate from '@/components/common/Sidebar/Separate'
|
||||
import Submenu from '@/components/common/Sidebar/Submenu'
|
||||
import Footer from '@/components/common/Sidebar/Footer'
|
||||
|
||||
export const SidebarContext = createContext({ isCollapse: false })
|
||||
|
||||
interface SidebarProps extends PropsWithChildren {
|
||||
title: string
|
||||
width?: string
|
||||
@@ -17,35 +19,38 @@ interface SidebarProps extends PropsWithChildren {
|
||||
}
|
||||
|
||||
const Sidebar = (props: SidebarProps) => {
|
||||
const [isHideSidebar, setIsHideSidebar] = useState(getLocalStorage('HIDE_SIDEBAR') === 'true')
|
||||
const { styles, cx } = useStyles()
|
||||
const [isCollapseSidebar, setIsCollapseSidebar] = useState(
|
||||
getLocalStorage('COLLAPSE_SIDEBAR') === 'true'
|
||||
)
|
||||
|
||||
const switchSidebar = () => {
|
||||
setLocalStorage('HIDE_SIDEBAR', !isHideSidebar ? 'true' : 'false')
|
||||
setIsHideSidebar(!isHideSidebar)
|
||||
props.onSidebarSwitch?.(isHideSidebar)
|
||||
setLocalStorage('COLLAPSE_SIDEBAR', !isCollapseSidebar ? 'true' : 'false')
|
||||
setIsCollapseSidebar(!isCollapseSidebar)
|
||||
props.onSidebarSwitch?.(isCollapseSidebar)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarContext.Provider value={{ isCollapse: isCollapseSidebar }}>
|
||||
<div
|
||||
className={`${styles.sidebar}${isHideSidebar ? ` ${styles.hide}` : ''}`}
|
||||
className={cx(styles.sidebar, isCollapseSidebar ? styles.collapse : '')}
|
||||
style={{ width: props.width ?? 'clamp(180px, 20vw, 240px)' }}
|
||||
>
|
||||
<div className={styles.title}>
|
||||
<span className={styles.iconBox} onClick={switchSidebar}>
|
||||
<span className={styles.titleIcon} onClick={switchSidebar}>
|
||||
<Icon component={IconOxygenExpand} />
|
||||
</span>
|
||||
<span className={styles.text}>{props.title}</span>
|
||||
<span className={styles.titleText}>{props.title}</span>
|
||||
</div>
|
||||
<Separate style={{ marginTop: 0 }} />
|
||||
<div className={styles.content}>{props.children}</div>
|
||||
<div className={styles.bottomFixed} style={{ flex: 'none' }}>
|
||||
<div className={styles.content} style={{ flex: 'none' }}>
|
||||
{props.bottomFixed}
|
||||
</div>
|
||||
<Separate style={{ marginTop: 0, marginBottom: 0 }} />
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
</SidebarContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import styles from '@/assets/css/components/common/url-card.module.less'
|
||||
import useStyles from '@/assets/css/components/common/url-card.style'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
|
||||
@@ -26,6 +26,7 @@ const UrlCard = ({
|
||||
children,
|
||||
...props
|
||||
}: UrlCardProps) => {
|
||||
const { styles } = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -39,13 +40,12 @@ const UrlCard = ({
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.root}
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
{...props}
|
||||
ref={cardRef}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={styles.urlCard}>
|
||||
<FlexBox className={styles.root}>
|
||||
<Icon component={icon} className={styles.icon} />
|
||||
<div className={styles.text}>{children}</div>
|
||||
<div>{description}</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { HandleContextInst } from '@/components/dnd/HandleContext'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/dnd/drag-handle.module.less'
|
||||
import useStyles from '@/assets/css/components/dnd/drag-handle.style'
|
||||
|
||||
interface DragHandleProps {
|
||||
padding?: string | number
|
||||
}
|
||||
|
||||
const DragHandle = ({ padding }: DragHandleProps) => {
|
||||
const { styles } = useStyles()
|
||||
const { attributes, listeners, ref } = useContext(HandleContextInst)
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/dnd/drop-mask.module.less'
|
||||
import useStyles from '@/assets/css/components/dnd/drop-mask.style'
|
||||
|
||||
const DropMask = () => {
|
||||
const { styles } = useStyles()
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.dropMaskBorder}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CSSProperties, PropsWithChildren } from 'react'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import useStyles from '@/assets/css/components/dnd/sortable.style'
|
||||
import { HandleContext, HandleContextInst } from '@/components/dnd/HandleContext'
|
||||
|
||||
interface SortableProps extends PropsWithChildren {
|
||||
@@ -9,6 +10,7 @@ interface SortableProps extends PropsWithChildren {
|
||||
}
|
||||
|
||||
const Sortable = ({ id, data, isDelete, children }: SortableProps) => {
|
||||
const { styles } = useStyles()
|
||||
const {
|
||||
attributes,
|
||||
isDragging,
|
||||
@@ -43,7 +45,7 @@ const Sortable = ({ id, data, isDelete, children }: SortableProps) => {
|
||||
<div
|
||||
ref={draggableRef}
|
||||
style={style}
|
||||
className={isDragging && isDelete ? 'delete' : undefined}
|
||||
className={isDragging && isDelete ? styles.delete : undefined}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/system/setting-card.module.less'
|
||||
import useStyles from '@/assets/css/components/system/setting-card.style'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import Permission from '@/components/common/Permission'
|
||||
@@ -16,9 +16,11 @@ interface SettingsCardProps extends PropsWithChildren {
|
||||
onSave?: () => void
|
||||
}
|
||||
export const SettingsCard = (props: SettingsCardProps) => {
|
||||
const { styles } = useStyles()
|
||||
|
||||
return (
|
||||
<Card className={styles.root}>
|
||||
<FlexBox className={styles.settingsCard}>
|
||||
<Card>
|
||||
<FlexBox className={styles.root}>
|
||||
<FlexBox direction={'horizontal'} className={styles.head}>
|
||||
<Icon component={props.icon} className={styles.icon} />
|
||||
<div className={styles.title}>{props.title}</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/system/statistics-card.module.less'
|
||||
import useStyles from '@/assets/css/components/system/statistics-card.style'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import LoadingMask from '@/components/common/LoadingMask'
|
||||
@@ -13,9 +13,11 @@ interface StatisticsCardProps extends PropsWithChildren {
|
||||
}
|
||||
|
||||
export const StatisticsCard = (props: StatisticsCardProps) => {
|
||||
const { styles } = useStyles()
|
||||
|
||||
return (
|
||||
<Card className={styles.root} style={{ overflow: 'visible' }}>
|
||||
<FlexBox className={styles.statisticsCard}>
|
||||
<Card style={{ overflow: 'visible' }}>
|
||||
<FlexBox className={styles.root}>
|
||||
<FlexBox direction={'horizontal'} className={styles.head}>
|
||||
<Icon component={props.icon} className={styles.icon} />
|
||||
<div className={styles.title}>{props.title}</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import VanillaTilt from 'vanilla-tilt'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/tools/load-more-card.module.less'
|
||||
import useStyles from '@/assets/css/components/tools/load-more-card.style'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import Card from '@/components/common/Card'
|
||||
|
||||
@@ -9,6 +9,7 @@ interface LoadMoreCardProps {
|
||||
}
|
||||
|
||||
const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||
const { styles } = useStyles()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -23,13 +24,8 @@ const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.root}
|
||||
style={{ overflow: 'visible' }}
|
||||
ref={cardRef}
|
||||
onClick={onClick}
|
||||
>
|
||||
<FlexBox className={styles.loadMoreCard}>
|
||||
<Card ref={cardRef} onClick={onClick}>
|
||||
<FlexBox className={styles.root}>
|
||||
<div className={styles.icon}>
|
||||
<Icon component={IconOxygenMore} />{' '}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import styles from '@/assets/css/components/tools/repository-card.module.less'
|
||||
import useStyles from '@/assets/css/components/tools/repository-card.style'
|
||||
import { omitTextByByte } from '@/util/common'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import Draggable from '@/components/dnd/Draggable'
|
||||
@@ -11,6 +12,7 @@ interface RepositoryCardProps
|
||||
icon: string
|
||||
toolName: string
|
||||
toolId: string
|
||||
toolDesc?: string
|
||||
ver: string
|
||||
platform: Platform
|
||||
options?: TiltOptions
|
||||
@@ -28,6 +30,7 @@ const RepositoryCard = ({
|
||||
icon,
|
||||
toolName,
|
||||
toolId,
|
||||
toolDesc,
|
||||
ver,
|
||||
platform,
|
||||
options = {
|
||||
@@ -46,6 +49,7 @@ const RepositoryCard = ({
|
||||
children,
|
||||
...props
|
||||
}: RepositoryCardProps) => {
|
||||
const { styles } = useStyles()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -57,13 +61,8 @@ const RepositoryCard = ({
|
||||
id={`!:${toolId}:${ver}:${platform}`}
|
||||
data={{ icon, toolName, toolId, authorUsername: '!', ver, platform }}
|
||||
>
|
||||
<Card
|
||||
className={styles.root}
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
>
|
||||
<FlexBox className={styles.repositoryCard}>
|
||||
<Card style={{ ...style }} ref={cardRef} {...props}>
|
||||
<FlexBox className={styles.root}>
|
||||
<div className={styles.header}>
|
||||
{children}
|
||||
<DragHandle />
|
||||
@@ -72,8 +71,13 @@ const RepositoryCard = ({
|
||||
<img src={`data:image/svg+xml;base64,${icon}`} alt={'Icon'} />
|
||||
</div>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.toolName}>{toolName}</div>
|
||||
<div className={styles.toolName} title={toolName}>
|
||||
{toolName}
|
||||
</div>
|
||||
<div>{`ID: ${toolId}`}</div>
|
||||
<div className={styles.toolDesc} title={toolDesc}>
|
||||
{toolDesc && omitTextByByte(toolDesc, 64)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.operation}>
|
||||
{onOpen && (
|
||||
|
||||
@@ -2,9 +2,8 @@ import { DetailedHTMLProps, HTMLAttributes, MouseEvent } from 'react'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import protocolCheck from 'custom-protocol-check'
|
||||
import Icon from '@ant-design/icons'
|
||||
import styles from '@/assets/css/components/tools/store-card.module.less'
|
||||
import { COLOR_BACKGROUND, COLOR_MAIN, COLOR_PRODUCTION } from '@/constants/common.constants'
|
||||
import { checkDesktop, omitText } from '@/util/common'
|
||||
import useStyles from '@/assets/css/components/tools/store-card.style'
|
||||
import { checkDesktop, omitTextByByte } from '@/util/common'
|
||||
import { getLoginStatus, getUserId } from '@/util/auth'
|
||||
import {
|
||||
getAndroidUrl,
|
||||
@@ -35,7 +34,6 @@ interface StoreCardProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement
|
||||
|
||||
const StoreCard = ({
|
||||
style,
|
||||
|
||||
ref,
|
||||
icon,
|
||||
toolName,
|
||||
@@ -56,6 +54,7 @@ const StoreCard = ({
|
||||
favorite,
|
||||
...props
|
||||
}: StoreCardProps) => {
|
||||
const { styles, theme } = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const [modal, contextHolder] = AntdModal.useModal()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
@@ -77,7 +76,7 @@ const StoreCard = ({
|
||||
if (platform === 'ANDROID') {
|
||||
void modal.confirm({
|
||||
centered: true,
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
icon: <Icon style={{ color: theme.colorPrimary }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
content: (
|
||||
<FlexBox className={styles.androidQrcode}>
|
||||
@@ -115,8 +114,8 @@ const StoreCard = ({
|
||||
if (favorite_) {
|
||||
void r_tool_remove_favorite({
|
||||
authorId: author.id,
|
||||
toolId: toolId,
|
||||
platform: platform
|
||||
toolId,
|
||||
platform
|
||||
}).then((res) => {
|
||||
const response = res.data
|
||||
if (response.success) {
|
||||
@@ -128,8 +127,8 @@ const StoreCard = ({
|
||||
} else {
|
||||
void r_tool_add_favorite({
|
||||
authorId: author.id,
|
||||
toolId: toolId,
|
||||
platform: platform
|
||||
toolId,
|
||||
platform
|
||||
}).then((res) => {
|
||||
const response = res.data
|
||||
if (response.success) {
|
||||
@@ -145,7 +144,7 @@ const StoreCard = ({
|
||||
e.stopPropagation()
|
||||
void modal.confirm({
|
||||
centered: true,
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
icon: <Icon style={{ color: theme.colorPrimary }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
content: (
|
||||
<FlexBox className={styles.androidQrcode}>
|
||||
@@ -200,17 +199,16 @@ const StoreCard = ({
|
||||
toolId,
|
||||
authorUsername: author.username,
|
||||
ver: '',
|
||||
platform: platform
|
||||
platform
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
className={styles.root}
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={styles.storeCard}>
|
||||
<FlexBox className={styles.root}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.version}>
|
||||
<AntdTag>
|
||||
@@ -255,7 +253,7 @@ const StoreCard = ({
|
||||
favorite_ ? IconOxygenStarFilled : IconOxygenStar
|
||||
}
|
||||
style={{
|
||||
color: favorite_ ? COLOR_PRODUCTION : undefined
|
||||
color: favorite_ ? theme.colorPrimary : undefined
|
||||
}}
|
||||
onClick={handleOnStarBtnClick}
|
||||
/>
|
||||
@@ -268,13 +266,14 @@ const StoreCard = ({
|
||||
<img src={`data:image/svg+xml;base64,${icon}`} alt={'Icon'} />
|
||||
</div>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.toolName}>{toolName}</div>
|
||||
<div className={styles.toolName} title={toolName}>
|
||||
{toolName}
|
||||
</div>
|
||||
<div>{`ID: ${toolId}`}</div>
|
||||
{toolDesc && (
|
||||
<div
|
||||
className={styles.toolDesc}
|
||||
title={toolDesc}
|
||||
>{`简介:${omitText(toolDesc, 18)}`}</div>
|
||||
<div className={styles.toolDesc} title={toolDesc}>
|
||||
{omitTextByByte(toolDesc, 64)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showAuthor && (
|
||||
@@ -288,7 +287,7 @@ const StoreCard = ({
|
||||
alt={'Avatar'}
|
||||
/>
|
||||
}
|
||||
style={{ background: COLOR_BACKGROUND }}
|
||||
style={{ background: theme.colorBgLayout }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.authorName}>{author.userInfo.nickname}</div>
|
||||
|
||||
Reference in New Issue
Block a user