code format
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import {FC} from 'react';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import {Dropdown, Option} from '@fluentui/react-components';
|
||||
import { FC } from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Dropdown, Option } from '@fluentui/react-components';
|
||||
import commonStore from '../stores/commonStore';
|
||||
|
||||
export const ConfigSelector: FC<{ size?: 'small' | 'medium' | 'large' }> = observer(({size}) => {
|
||||
return <Dropdown size={size} style={{minWidth: 0}} listbox={{style: {minWidth: 0}}}
|
||||
value={commonStore.getCurrentModelConfig().name}
|
||||
selectedOptions={[commonStore.currentModelConfigIndex.toString()]}
|
||||
onOptionSelect={(_, data) => {
|
||||
if (data.optionValue)
|
||||
commonStore.setCurrentConfigIndex(Number(data.optionValue));
|
||||
}}>
|
||||
export const ConfigSelector: FC<{ size?: 'small' | 'medium' | 'large' }> = observer(({ size }) => {
|
||||
return <Dropdown size={size} style={{ minWidth: 0 }} listbox={{ style: { minWidth: 0 } }}
|
||||
value={commonStore.getCurrentModelConfig().name}
|
||||
selectedOptions={[commonStore.currentModelConfigIndex.toString()]}
|
||||
onOptionSelect={(_, data) => {
|
||||
if (data.optionValue)
|
||||
commonStore.setCurrentConfigIndex(Number(data.optionValue));
|
||||
}}>
|
||||
{commonStore.modelConfigs.map((config, index) =>
|
||||
<Option key={index} value={index.toString()}>{config.name}</Option>
|
||||
)}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import {FC, useState} from 'react';
|
||||
import {CheckIcon, CopyIcon} from '@primer/octicons-react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ClipboardSetText} from '../../wailsjs/runtime';
|
||||
import {ToolTipButton} from './ToolTipButton';
|
||||
import { FC, useState } from 'react';
|
||||
import { CheckIcon, CopyIcon } from '@primer/octicons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ClipboardSetText } from '../../wailsjs/runtime';
|
||||
import { ToolTipButton } from './ToolTipButton';
|
||||
|
||||
export const CopyButton: FC<{ content: string }> = ({content}) => {
|
||||
const {t} = useTranslation();
|
||||
export const CopyButton: FC<{ content: string }> = ({ content }) => {
|
||||
const { t } = useTranslation();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const onClick = () => {
|
||||
ClipboardSetText(content)
|
||||
.then(() => setCopied(true))
|
||||
.then(() =>
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 600)
|
||||
);
|
||||
.then(() => setCopied(true))
|
||||
.then(() =>
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 600)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolTipButton desc={t('Copy')} size="small" appearance="subtle" icon={copied ? <CheckIcon/> : <CopyIcon/>}
|
||||
onClick={onClick}/>
|
||||
<ToolTipButton desc={t('Copy')} size="small" appearance="subtle" icon={copied ? <CheckIcon /> : <CopyIcon />}
|
||||
onClick={onClick} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {FC, ReactElement} from 'react';
|
||||
import {Label, Tooltip} from '@fluentui/react-components';
|
||||
import { FC, ReactElement } from 'react';
|
||||
import { Label, Tooltip } from '@fluentui/react-components';
|
||||
import classnames from 'classnames';
|
||||
|
||||
export const Labeled: FC<{
|
||||
label: string; desc?: string | null, content: ReactElement, flex?: boolean, spaceBetween?: boolean
|
||||
}> = ({
|
||||
label,
|
||||
desc,
|
||||
content,
|
||||
flex,
|
||||
spaceBetween
|
||||
}) => {
|
||||
label,
|
||||
desc,
|
||||
content,
|
||||
flex,
|
||||
spaceBetween
|
||||
}) => {
|
||||
return (
|
||||
<div className={classnames(
|
||||
'items-center',
|
||||
|
||||
@@ -3,14 +3,14 @@ import rehypeRaw from 'rehype-raw';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkBreaks from 'remark-breaks';
|
||||
import {FC} from 'react';
|
||||
import {ReactMarkdownOptions} from 'react-markdown/lib/react-markdown';
|
||||
import {BrowserOpenURL} from '../../wailsjs/runtime';
|
||||
import { FC } from 'react';
|
||||
import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
|
||||
import { BrowserOpenURL } from '../../wailsjs/runtime';
|
||||
|
||||
const Hyperlink: FC<any> = ({href, children}) => {
|
||||
const Hyperlink: FC<any> = ({ href, children }) => {
|
||||
return (
|
||||
<span
|
||||
style={{color: '#8ab4f8', cursor: 'pointer'}}
|
||||
style={{ color: '#8ab4f8', cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
BrowserOpenURL(href);
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, {CSSProperties, FC} from 'react';
|
||||
import {Input} from '@fluentui/react-components';
|
||||
import {SliderOnChangeData} from '@fluentui/react-slider';
|
||||
import React, { CSSProperties, FC } from 'react';
|
||||
import { Input } from '@fluentui/react-components';
|
||||
import { SliderOnChangeData } from '@fluentui/react-slider';
|
||||
|
||||
export const NumberInput: FC<{
|
||||
value: number,
|
||||
@@ -9,23 +9,23 @@ export const NumberInput: FC<{
|
||||
step?: number,
|
||||
onChange?: (ev: React.ChangeEvent<HTMLInputElement>, data: SliderOnChangeData) => void
|
||||
style?: CSSProperties
|
||||
}> = ({value, min, max, step, onChange, style}) => {
|
||||
}> = ({ value, min, max, step, onChange, style }) => {
|
||||
return (
|
||||
<Input type="number" style={style} value={value.toString()} min={min} max={max} step={step}
|
||||
onChange={(e, data) => {
|
||||
onChange?.(e, {value: Number(data.value)});
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
if (onChange) {
|
||||
if (step) {
|
||||
const offset = (min > 0 ? min : 0) - (max < 0 ? max : 0);
|
||||
value = Number(((
|
||||
Math.round((value - offset) / step) * step)
|
||||
+ offset)
|
||||
.toFixed(2)); // avoid precision issues
|
||||
}
|
||||
onChange(e, {value: Math.max(Math.min(value, max), min)});
|
||||
}
|
||||
}}/>
|
||||
onChange={(e, data) => {
|
||||
onChange?.(e, { value: Number(data.value) });
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
if (onChange) {
|
||||
if (step) {
|
||||
const offset = (min > 0 ? min : 0) - (max < 0 ? max : 0);
|
||||
value = Number(((
|
||||
Math.round((value - offset) / step) * step)
|
||||
+ offset)
|
||||
.toFixed(2)); // avoid precision issues
|
||||
}
|
||||
onChange(e, { value: Math.max(Math.min(value, max), min) });
|
||||
}
|
||||
}} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, {FC, ReactElement} from 'react';
|
||||
import {Divider, Text} from '@fluentui/react-components';
|
||||
import React, { FC, ReactElement } from 'react';
|
||||
import { Divider, Text } from '@fluentui/react-components';
|
||||
|
||||
export const Page: FC<{ title: string; content: ReactElement }> = ({title, content}) => {
|
||||
export const Page: FC<{ title: string; content: ReactElement }> = ({ title, content }) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 p-2 h-full">
|
||||
<Text size={600}>{title}</Text>
|
||||
<Divider style={{flexGrow: 0}}/>
|
||||
<Divider style={{ flexGrow: 0 }} />
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {FC, useState} from 'react';
|
||||
import {MuteIcon, UnmuteIcon} from '@primer/octicons-react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ToolTipButton} from './ToolTipButton';
|
||||
import { FC, useState } from 'react';
|
||||
import { MuteIcon, UnmuteIcon } from '@primer/octicons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ToolTipButton } from './ToolTipButton';
|
||||
import commonStore from '../stores/commonStore';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
const synth = window.speechSynthesis;
|
||||
|
||||
export const ReadButton: FC<{ content: string }> = observer(({content}) => {
|
||||
const {t} = useTranslation();
|
||||
export const ReadButton: FC<{ content: string }> = observer(({ content }) => {
|
||||
const { t } = useTranslation();
|
||||
const [speaking, setSpeaking] = useState(false);
|
||||
let lang: string = commonStore.settings.language;
|
||||
if (lang === 'dev')
|
||||
@@ -46,7 +46,8 @@ export const ReadButton: FC<{ content: string }> = observer(({content}) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle" icon={speaking ? <MuteIcon/> : <UnmuteIcon/>}
|
||||
onClick={speaking ? stopSpeak : startSpeak}/>
|
||||
<ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle"
|
||||
icon={speaking ? <MuteIcon /> : <UnmuteIcon />}
|
||||
onClick={speaking ? stopSpeak : startSpeak} />
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import React, { FC, MouseEventHandler, ReactElement } from 'react';
|
||||
import commonStore, { ModelStatus } from '../stores/commonStore';
|
||||
import { AddToDownloadList, DepCheck, FileExists, InstallPyDep, StartServer } from '../../wailsjs/go/backend_golang/App';
|
||||
import {
|
||||
AddToDownloadList,
|
||||
DepCheck,
|
||||
FileExists,
|
||||
InstallPyDep,
|
||||
StartServer
|
||||
} from '../../wailsjs/go/backend_golang/App';
|
||||
import { Button } from '@fluentui/react-components';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { exit, readRoot, switchModel, updateConfig } from '../apis';
|
||||
@@ -29,141 +35,141 @@ const iconModeButtonIcon: { [modelStatus: number]: ReactElement } = {
|
||||
|
||||
export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean }>
|
||||
= observer(({
|
||||
onClickRun,
|
||||
iconMode
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
onClickRun,
|
||||
iconMode
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onClickMainButton = async () => {
|
||||
if (commonStore.modelStatus === ModelStatus.Offline) {
|
||||
commonStore.setModelStatus(ModelStatus.Starting);
|
||||
const onClickMainButton = async () => {
|
||||
if (commonStore.modelStatus === ModelStatus.Offline) {
|
||||
commonStore.setModelStatus(ModelStatus.Starting);
|
||||
|
||||
const modelConfig = commonStore.getCurrentModelConfig();
|
||||
let modelName = ''
|
||||
let modelPath = ''
|
||||
if (modelConfig && modelConfig.modelParameters) {
|
||||
modelName = modelConfig.modelParameters.modelName;
|
||||
modelPath = `./${manifest.localModelDir}/${modelName}`;
|
||||
} else {
|
||||
toast(t('Model Config Exception'), { type: 'error' });
|
||||
const modelConfig = commonStore.getCurrentModelConfig();
|
||||
let modelName = '';
|
||||
let modelPath = '';
|
||||
if (modelConfig && modelConfig.modelParameters) {
|
||||
modelName = modelConfig.modelParameters.modelName;
|
||||
modelPath = `./${manifest.localModelDir}/${modelName}`;
|
||||
} else {
|
||||
toast(t('Model Config Exception'), { type: 'error' });
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!commonStore.depComplete) {
|
||||
let depErrorMsg = '';
|
||||
await DepCheck().catch((e) => {
|
||||
depErrorMsg = e.message || e;
|
||||
WindowShow();
|
||||
if (depErrorMsg === 'python zip not found') {
|
||||
toastWithButton(t('Python target not found, would you like to download it?'), t('Download'), () => {
|
||||
toastWithButton(`${t('Downloading')} Python`, t('Check'), () => {
|
||||
navigate({ pathname: '/downloads' });
|
||||
}, { autoClose: 3000 });
|
||||
AddToDownloadList('python-3.10.11-embed-amd64.zip', 'https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip');
|
||||
});
|
||||
} else if (depErrorMsg.includes('DepCheck Error')) {
|
||||
toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
|
||||
InstallPyDep(commonStore.settings.cnMirror);
|
||||
setTimeout(WindowShow, 1000);
|
||||
});
|
||||
} else {
|
||||
toast(depErrorMsg, { type: 'error' });
|
||||
}
|
||||
});
|
||||
if (depErrorMsg) {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
return;
|
||||
}
|
||||
commonStore.setDepComplete(true);
|
||||
saveCache();
|
||||
}
|
||||
|
||||
if (!commonStore.depComplete) {
|
||||
let depErrorMsg = '';
|
||||
await DepCheck().catch((e) => {
|
||||
depErrorMsg = e.message || e;
|
||||
WindowShow();
|
||||
if (depErrorMsg === 'python zip not found') {
|
||||
toastWithButton(t('Python target not found, would you like to download it?'), t('Download'), () => {
|
||||
toastWithButton(`${t('Downloading')} Python`, t('Check'), () => {
|
||||
navigate({ pathname: '/downloads' });
|
||||
}, { autoClose: 3000 });
|
||||
AddToDownloadList('python-3.10.11-embed-amd64.zip', 'https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip');
|
||||
});
|
||||
} else if (depErrorMsg.includes('DepCheck Error')) {
|
||||
toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
|
||||
InstallPyDep(commonStore.settings.cnMirror);
|
||||
setTimeout(WindowShow, 1000)
|
||||
});
|
||||
} else {
|
||||
toast(depErrorMsg, { type: 'error' });
|
||||
}
|
||||
});
|
||||
if (depErrorMsg) {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
return;
|
||||
}
|
||||
commonStore.setDepComplete(true);
|
||||
saveCache();
|
||||
}
|
||||
|
||||
if (!await FileExists(modelPath)) {
|
||||
toastWithButton(t('Model file not found'), t('Download'), () => {
|
||||
const downloadUrl = commonStore.modelSourceList.find(item => item.name === modelName)?.downloadUrl;
|
||||
if (downloadUrl) {
|
||||
toastWithButton(`${t('Downloading')} ${modelName}`, t('Check'), () => {
|
||||
if (!await FileExists(modelPath)) {
|
||||
toastWithButton(t('Model file not found'), t('Download'), () => {
|
||||
const downloadUrl = commonStore.modelSourceList.find(item => item.name === modelName)?.downloadUrl;
|
||||
if (downloadUrl) {
|
||||
toastWithButton(`${t('Downloading')} ${modelName}`, t('Check'), () => {
|
||||
navigate({ pathname: '/downloads' });
|
||||
},
|
||||
{ autoClose: 3000 });
|
||||
AddToDownloadList(modelPath, downloadUrl);
|
||||
} else {
|
||||
toast(t('Can not find download url'), { type: 'error' });
|
||||
}
|
||||
});
|
||||
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
return;
|
||||
}
|
||||
|
||||
const port = modelConfig.apiParameters.apiPort;
|
||||
|
||||
await exit(1000).catch(() => {
|
||||
{ autoClose: 3000 });
|
||||
AddToDownloadList(modelPath, downloadUrl);
|
||||
} else {
|
||||
toast(t('Can not find download url'), { type: 'error' });
|
||||
}
|
||||
});
|
||||
StartServer(port);
|
||||
setTimeout(WindowShow, 1000)
|
||||
|
||||
let timeoutCount = 6;
|
||||
let loading = false;
|
||||
const intervalId = setInterval(() => {
|
||||
readRoot()
|
||||
.then(r => {
|
||||
if (r.ok && !loading) {
|
||||
clearInterval(intervalId);
|
||||
commonStore.setModelStatus(ModelStatus.Loading);
|
||||
loading = true;
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
return;
|
||||
}
|
||||
|
||||
const port = modelConfig.apiParameters.apiPort;
|
||||
|
||||
await exit(1000).catch(() => {
|
||||
});
|
||||
StartServer(port);
|
||||
setTimeout(WindowShow, 1000);
|
||||
|
||||
let timeoutCount = 6;
|
||||
let loading = false;
|
||||
const intervalId = setInterval(() => {
|
||||
readRoot()
|
||||
.then(r => {
|
||||
if (r.ok && !loading) {
|
||||
clearInterval(intervalId);
|
||||
commonStore.setModelStatus(ModelStatus.Loading);
|
||||
loading = true;
|
||||
toast(t('Loading Model'), { type: 'info' });
|
||||
updateConfig({
|
||||
max_tokens: modelConfig.apiParameters.maxResponseToken,
|
||||
temperature: modelConfig.apiParameters.temperature,
|
||||
top_p: modelConfig.apiParameters.topP,
|
||||
presence_penalty: modelConfig.apiParameters.presencePenalty,
|
||||
frequency_penalty: modelConfig.apiParameters.frequencyPenalty
|
||||
});
|
||||
switchModel({
|
||||
model: `${manifest.localModelDir}/${modelConfig.modelParameters.modelName}`,
|
||||
strategy: getStrategy(modelConfig)
|
||||
}).then((r) => {
|
||||
if (r.ok) {
|
||||
commonStore.setModelStatus(ModelStatus.Working);
|
||||
toastWithButton(t('Startup Completed'), t('Chat'), () => {
|
||||
navigate({ pathname: '/chat' });
|
||||
}, { type: 'success', autoClose: 3000 });
|
||||
} else if (r.status === 304) {
|
||||
toast(t('Loading Model'), { type: 'info' });
|
||||
updateConfig({
|
||||
max_tokens: modelConfig.apiParameters.maxResponseToken,
|
||||
temperature: modelConfig.apiParameters.temperature,
|
||||
top_p: modelConfig.apiParameters.topP,
|
||||
presence_penalty: modelConfig.apiParameters.presencePenalty,
|
||||
frequency_penalty: modelConfig.apiParameters.frequencyPenalty
|
||||
});
|
||||
switchModel({
|
||||
model: `${manifest.localModelDir}/${modelConfig.modelParameters.modelName}`,
|
||||
strategy: getStrategy(modelConfig)
|
||||
}).then((r) => {
|
||||
if (r.ok) {
|
||||
commonStore.setModelStatus(ModelStatus.Working);
|
||||
toastWithButton(t('Startup Completed'), t('Chat'), () => {
|
||||
navigate({ pathname: '/chat' });
|
||||
}, { type: 'success', autoClose: 3000 });
|
||||
} else if (r.status === 304) {
|
||||
toast(t('Loading Model'), { type: 'info' });
|
||||
} else {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
toast(t('Failed to switch model'), { type: 'error' });
|
||||
}
|
||||
}).catch(() => {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
toast(t('Failed to switch model'), { type: 'error' });
|
||||
});
|
||||
} else {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
toast(t('Failed to switch model'), { type: 'error' });
|
||||
}
|
||||
}).catch(() => {
|
||||
if (timeoutCount <= 0) {
|
||||
clearInterval(intervalId);
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
}
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
toast(t('Failed to switch model'), { type: 'error' });
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
if (timeoutCount <= 0) {
|
||||
clearInterval(intervalId);
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
}
|
||||
});
|
||||
|
||||
timeoutCount--;
|
||||
}, 1000);
|
||||
} else {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
exit();
|
||||
}
|
||||
};
|
||||
timeoutCount--;
|
||||
}, 1000);
|
||||
} else {
|
||||
commonStore.setModelStatus(ModelStatus.Offline);
|
||||
exit();
|
||||
}
|
||||
};
|
||||
|
||||
const onClick = async (e: any) => {
|
||||
if (commonStore.modelStatus === ModelStatus.Offline)
|
||||
await onClickRun?.(e);
|
||||
await onClickMainButton();
|
||||
};
|
||||
const onClick = async (e: any) => {
|
||||
if (commonStore.modelStatus === ModelStatus.Offline)
|
||||
await onClickRun?.(e);
|
||||
await onClickMainButton();
|
||||
};
|
||||
|
||||
return (iconMode ?
|
||||
return (iconMode ?
|
||||
<ToolTipButton disabled={commonStore.modelStatus === ModelStatus.Starting}
|
||||
icon={iconModeButtonIcon[commonStore.modelStatus]}
|
||||
desc={t(mainButtonText[commonStore.modelStatus])}
|
||||
@@ -173,5 +179,5 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
|
||||
onClick={onClick}>
|
||||
{t(mainButtonText[commonStore.modelStatus])}
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {FC, ReactElement} from 'react';
|
||||
import {Card, Text} from '@fluentui/react-components';
|
||||
import { FC, ReactElement } from 'react';
|
||||
import { Card, Text } from '@fluentui/react-components';
|
||||
|
||||
export const Section: FC<{
|
||||
title: string; desc?: string | null, content: ReactElement, outline?: boolean
|
||||
}> =
|
||||
({title, desc, content, outline = true}) => {
|
||||
({ title, desc, content, outline = true }) => {
|
||||
return (
|
||||
<Card size="small" appearance={outline ? 'outline' : 'subtle'}>
|
||||
<div className="flex flex-col gap-5">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, {FC, MouseEventHandler, ReactElement} from 'react';
|
||||
import {Button, Tooltip} from '@fluentui/react-components';
|
||||
import React, { FC, MouseEventHandler, ReactElement } from 'react';
|
||||
import { Button, Tooltip } from '@fluentui/react-components';
|
||||
|
||||
export const ToolTipButton: FC<{
|
||||
text?: string | null,
|
||||
@@ -11,19 +11,19 @@ export const ToolTipButton: FC<{
|
||||
disabled?: boolean,
|
||||
onClick?: MouseEventHandler
|
||||
}> = ({
|
||||
text,
|
||||
desc,
|
||||
icon,
|
||||
size,
|
||||
shape,
|
||||
appearance,
|
||||
disabled,
|
||||
onClick
|
||||
}) => {
|
||||
text,
|
||||
desc,
|
||||
icon,
|
||||
size,
|
||||
shape,
|
||||
appearance,
|
||||
disabled,
|
||||
onClick
|
||||
}) => {
|
||||
return (
|
||||
<Tooltip content={desc} showDelay={0} hideDelay={0} relationship="label">
|
||||
<Button disabled={disabled} icon={icon} onClick={onClick} size={size} shape={shape}
|
||||
appearance={appearance}>{text}</Button>
|
||||
appearance={appearance}>{text}</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {FC, useEffect, useRef} from 'react';
|
||||
import {Slider, Text} from '@fluentui/react-components';
|
||||
import {SliderOnChangeData} from '@fluentui/react-slider';
|
||||
import {NumberInput} from './NumberInput';
|
||||
import React, { FC, useEffect, useRef } from 'react';
|
||||
import { Slider, Text } from '@fluentui/react-components';
|
||||
import { SliderOnChangeData } from '@fluentui/react-slider';
|
||||
import { NumberInput } from './NumberInput';
|
||||
|
||||
export const ValuedSlider: FC<{
|
||||
value: number,
|
||||
@@ -10,7 +10,7 @@ export const ValuedSlider: FC<{
|
||||
step?: number,
|
||||
input?: boolean
|
||||
onChange?: (ev: React.ChangeEvent<HTMLInputElement>, data: SliderOnChangeData) => void
|
||||
}> = ({value, min, max, step, input, onChange}) => {
|
||||
}> = ({ value, min, max, step, input, onChange }) => {
|
||||
const sliderRef = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
if (step && sliderRef.current && sliderRef.current.parentElement) {
|
||||
@@ -21,11 +21,11 @@ export const ValuedSlider: FC<{
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Slider ref={sliderRef} className="grow" style={{minWidth: '50%'}} value={value} min={min}
|
||||
max={max} step={step}
|
||||
onChange={onChange}/>
|
||||
<Slider ref={sliderRef} className="grow" style={{ minWidth: '50%' }} value={value} min={min}
|
||||
max={max} step={step}
|
||||
onChange={onChange} />
|
||||
{input
|
||||
? <NumberInput style={{minWidth: 0}} value={value} min={min} max={max} step={step} onChange={onChange}/>
|
||||
? <NumberInput style={{ minWidth: 0 }} value={value} min={min} max={max} step={step} onChange={onChange} />
|
||||
: <Text>{value}</Text>}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user