mirror of
https://git.um-react.app/um/um-react.git
synced 2025-11-28 03:23:02 +00:00
refactor: batch 3
This commit is contained in:
@@ -1,24 +1,46 @@
|
||||
import type { RefObject } from 'react';
|
||||
import { MdAdd, MdDeleteForever, MdFileUpload } from 'react-icons/md';
|
||||
|
||||
export interface AddKeyProps {
|
||||
addKey: () => void;
|
||||
importKeyFromFile?: () => void;
|
||||
clearKeys?: () => void;
|
||||
refContainer?: RefObject<HTMLElement | null>;
|
||||
}
|
||||
|
||||
export function AddKey({ addKey, importKeyFromFile, clearKeys }: AddKeyProps) {
|
||||
export function AddKey({ addKey, refContainer, importKeyFromFile, clearKeys }: AddKeyProps) {
|
||||
const scrollToLastKey = () => {
|
||||
const container = refContainer?.current;
|
||||
if (container) {
|
||||
const inputs = container.querySelectorAll('input[data-name="key-input--name"]');
|
||||
const lastInput = inputs[inputs.length - 1] as HTMLInputElement | null;
|
||||
if (lastInput) {
|
||||
lastInput.focus({ preventScroll: true });
|
||||
lastInput.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddKey = () => {
|
||||
addKey();
|
||||
setTimeout(scrollToLastKey);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<div className="join">
|
||||
<button className="btn join-item" onClick={addKey}>
|
||||
<MdAdd /> 添加一条
|
||||
<button type="button" className="join-item btn flex items-center gap-2" onClick={handleAddKey}>
|
||||
<MdAdd className="text-lg" /> 添加一条
|
||||
</button>
|
||||
<button className="btn join-item" onClick={importKeyFromFile}>
|
||||
<MdFileUpload />
|
||||
<button type="button" className="join-item btn flex items-center gap-2" onClick={importKeyFromFile}>
|
||||
<MdFileUpload className="text-lg" />
|
||||
导入数据库…
|
||||
</button>
|
||||
<button className="btn btn-error join-item" onClick={clearKeys}>
|
||||
<MdDeleteForever />
|
||||
<button type="button" className="join-item btn flex items-center gap-2 btn-error" onClick={clearKeys}>
|
||||
<MdDeleteForever className="text-lg" />
|
||||
清空密钥
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import hljsStyleGitHub from 'react-syntax-highlighter/dist/esm/styles/hljs/github';
|
||||
import { ExtLink } from '../ExtLink';
|
||||
import PowerShellAdbDumpCommandTemplate from './adb_dump.ps1?raw';
|
||||
import ShellAdbDumpCommandTemplate from './adb_dump.sh?raw';
|
||||
import { applyTemplate } from '~/util/applyTemplate';
|
||||
|
||||
export interface AdbInstructionTemplateProps {
|
||||
dir: string;
|
||||
file: string;
|
||||
platform: 'win32' | 'linux';
|
||||
}
|
||||
|
||||
const URL_USB_DEBUGGING = 'https://developer.android.com/studio/debug/dev-options?hl=zh-cn#Enable-debugging';
|
||||
|
||||
const LANGUAGE_MAP = {
|
||||
win32: { language: 'ps1', template: PowerShellAdbDumpCommandTemplate },
|
||||
linux: { language: 'sh', template: ShellAdbDumpCommandTemplate },
|
||||
};
|
||||
|
||||
export function AdbInstructionTemplate({ dir, file, platform }: AdbInstructionTemplateProps) {
|
||||
const { language, template } = LANGUAGE_MAP[platform];
|
||||
const command = applyTemplate(template, { dir, file });
|
||||
|
||||
return (
|
||||
<ol className="list-decimal pl-4">
|
||||
<li>
|
||||
<p>
|
||||
确保 <code>adb</code> 命令可用。
|
||||
</p>
|
||||
|
||||
{platform === 'win32' && (
|
||||
<div>
|
||||
<span>
|
||||
💡 如果没有,可以
|
||||
<ExtLink href="https://scoop.sh/#/apps?q=adb">使用 Scoop 安装</ExtLink>。
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li>启动终端,进入 PowerShell 环境。</li>
|
||||
<li>
|
||||
在手机<ExtLink href={URL_USB_DEBUGGING}>启用 USB 调试</ExtLink>
|
||||
</li>
|
||||
<li>将安卓设备连接到电脑。</li>
|
||||
<li>
|
||||
<p>粘贴执行下述代码执行。若设备提示「是否允许 USB 调试」或「超级用户请求」,选择允许:</p>
|
||||
<SyntaxHighlighter language={language} style={hljsStyleGitHub}>
|
||||
{command}
|
||||
</SyntaxHighlighter>
|
||||
</li>
|
||||
<li>
|
||||
提交当前目录下的 <code>{file}</code> 文件。
|
||||
</li>
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
@@ -1,171 +1,73 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Code,
|
||||
Heading,
|
||||
ListItem,
|
||||
OrderedList,
|
||||
Text,
|
||||
chakra,
|
||||
} from '@chakra-ui/react';
|
||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import hljsStyleGitHub from 'react-syntax-highlighter/dist/esm/styles/hljs/github';
|
||||
|
||||
import PowerShellAdbDumpCommandTemplate from './adb_dump.ps1?raw';
|
||||
import ShellAdbDumpCommandTemplate from './adb_dump.sh?raw';
|
||||
import { ExtLink } from '../ExtLink';
|
||||
|
||||
const applyTemplate = (tpl: string, values: Record<string, unknown>) => {
|
||||
return tpl.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => (Object.hasOwn(values, key) ? String(values[key]) : '<nil>'));
|
||||
};
|
||||
import { Ruby } from '../Ruby';
|
||||
import { useId } from 'react';
|
||||
import { RootExplorerGuide } from './RootExplorerGuide';
|
||||
import { AdbInstructionTemplate } from './AdbInstructionTemplate';
|
||||
import { HiWord } from '../HelpText/HiWord';
|
||||
|
||||
export interface AndroidADBPullInstructionProps {
|
||||
dir: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
const URL_AMAZE = 'https://github.com/TeamAmaze/AmazeFileManager/releases/latest';
|
||||
const URL_MT2 = 'https://mt2.cn/download/';
|
||||
|
||||
export function AndroidADBPullInstruction({ dir, file }: AndroidADBPullInstructionProps) {
|
||||
const psAdbDumpCommand = applyTemplate(PowerShellAdbDumpCommandTemplate, { dir, file });
|
||||
const shAdbDumpCommand = applyTemplate(ShellAdbDumpCommandTemplate, { dir, file });
|
||||
const androidInstructionId = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>
|
||||
你需要
|
||||
<ruby>
|
||||
超级管理员
|
||||
<rp> (</rp>
|
||||
<rt>
|
||||
<code>root</code>
|
||||
</rt>
|
||||
<rp>)</rp>
|
||||
</ruby>
|
||||
访问权限来访问安卓应用的私有数据。
|
||||
</Text>
|
||||
<Text>
|
||||
<p>
|
||||
你需要<Ruby caption="root">超级管理员</Ruby>访问权限来访问安卓应用的私有数据。
|
||||
</p>
|
||||
<p>
|
||||
⚠️ 请注意,获取管理员权限通常意味着你的安卓设备
|
||||
<chakra.span color="red.400">将失去保修资格</chakra.span>。
|
||||
</Text>
|
||||
<HiWord>将失去保修资格</HiWord>。
|
||||
</p>
|
||||
|
||||
<Accordion allowToggle mt="2">
|
||||
<AccordionItem>
|
||||
<Heading as="h3" size="md">
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
在安卓手机端操作
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</Heading>
|
||||
<AccordionPanel pb={4}>
|
||||
<OrderedList>
|
||||
<ListItem>
|
||||
<Text>
|
||||
启动具有 <Code>root</Code> 特权的文件浏览器
|
||||
</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>
|
||||
访问 <Code>{dir}/</Code> 目录。
|
||||
</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>
|
||||
将文件 <Code>{file}</Code> 复制到浏览器可访问的目录。
|
||||
<br />
|
||||
(例如下载目录)
|
||||
</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>提交该数据库文件。</Text>
|
||||
</ListItem>
|
||||
</OrderedList>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<Heading as="h3" size="md">
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
在 PC 端操作(ADB / PowerShell)
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</Heading>
|
||||
<AccordionPanel pb={4}>
|
||||
<OrderedList>
|
||||
<ListItem>
|
||||
<Text>
|
||||
确保 <Code>adb</Code> 命令可用。
|
||||
</Text>
|
||||
<Text>
|
||||
💡 如果没有,可以
|
||||
<ExtLink href="https://scoop.sh/#/apps?q=adb">
|
||||
使用 Scoop 安装 <ExternalLinkIcon />
|
||||
</ExtLink>
|
||||
。
|
||||
</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>启动终端并进入 PowerShell 7 环境。</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>将安卓设备连接到电脑,并允许调试。</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>粘贴执行下述代码。若设备提示「超级用户请求」请允许:</Text>
|
||||
<SyntaxHighlighter language="ps1" style={hljsStyleGitHub}>
|
||||
{psAdbDumpCommand}
|
||||
</SyntaxHighlighter>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>
|
||||
提交当前目录下的 <Code>{file}</Code> 文件。
|
||||
</Text>
|
||||
</ListItem>
|
||||
</OrderedList>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<Heading as="h3" size="md">
|
||||
<AccordionButton>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
在 Linux / Mac 系统下操作(ADB / Shell)
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</Heading>
|
||||
<AccordionPanel pb={4}>
|
||||
<OrderedList>
|
||||
<ListItem>
|
||||
<Text>
|
||||
确保 <Code>adb</Code> 命令可用。
|
||||
</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>将安卓设备连接到电脑,并允许调试。</Text>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>粘贴执行下述代码。若设备提示「超级用户请求」请允许:</Text>
|
||||
<SyntaxHighlighter language="bash" style={hljsStyleGitHub}>
|
||||
{shAdbDumpCommand}
|
||||
</SyntaxHighlighter>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Text>
|
||||
提交当前目录下的 <Code>{file}</Code> 文件。
|
||||
</Text>
|
||||
</ListItem>
|
||||
</OrderedList>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<div className="join join-vertical bg-base-100 mt-2 max-w-full">
|
||||
<div className="collapse collapse-arrow join-item border-base-300 border">
|
||||
<input type="radio" name={androidInstructionId} />
|
||||
<div className="collapse-title font-semibold">在安卓手机端操作</div>
|
||||
<div className="collapse-content text-sm min-w-0">
|
||||
<ol className="list-decimal pl-4">
|
||||
<li>
|
||||
启动支持 <code>root</code> 特权的文件浏览器,如 <ExtLink href={URL_AMAZE}>Amaze 文件浏览器</ExtLink>、
|
||||
<ExtLink href={URL_MT2}>MT 管理器</ExtLink> 等。
|
||||
</li>
|
||||
<li>
|
||||
※ 记得启用 root 特权!
|
||||
<RootExplorerGuide />
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
访问 <code>{dir}/</code> 目录。
|
||||
</p>
|
||||
<p>※ 从侧边栏选择根目录开始。</p>
|
||||
</li>
|
||||
<li>
|
||||
将文件 <code>{file}</code> 复制到浏览器可访问的目录(例如下载目录)。
|
||||
</li>
|
||||
<li>提交该数据库文件。</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div className="collapse collapse-arrow join-item border-base-300 border">
|
||||
<input type="radio" name={androidInstructionId} />
|
||||
<div className="collapse-title font-semibold">在 PC 端操作(使用 ADB / PowerShell)</div>
|
||||
<div className="collapse-content text-sm min-w-0">
|
||||
<AdbInstructionTemplate dir={dir} file={file} platform="win32" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="collapse collapse-arrow join-item border-base-300 border">
|
||||
<input type="radio" name={androidInstructionId} />
|
||||
<div className="collapse-title font-semibold">在 Linux / Mac 系统下操作(使用 ADB / Shell)</div>
|
||||
<div className="collapse-content text-sm min-w-0">
|
||||
<AdbInstructionTemplate dir={dir} file={file} platform="linux" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { FiMenu, FiMoreVertical } from 'react-icons/fi';
|
||||
import { Header5 } from '../HelpText/Headers';
|
||||
import { Ruby } from '../Ruby';
|
||||
import { VQuote } from '../HelpText/VQuote';
|
||||
|
||||
export function RootExplorerGuide() {
|
||||
return (
|
||||
<div className="@container inline-flex flex-col items-start w-full pl-4">
|
||||
<div className="flex flex-col items-start gap-4 @md:flex-row">
|
||||
<div>
|
||||
<Header5 className="[&]:mt-0 [&]:pt-0">Amaze 文件浏览器</Header5>
|
||||
<ul className="ml-2 list-disc list-inside">
|
||||
<li>
|
||||
<div className="inline-flex items-center gap-1">
|
||||
点触主界面左上角的 <FiMenu /> 打开侧边栏
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
滑动到最底端,点触
|
||||
<VQuote>
|
||||
<Ruby caption="Settings">设置</Ruby>
|
||||
</VQuote>
|
||||
</li>
|
||||
<li>
|
||||
点触
|
||||
<VQuote>
|
||||
<Ruby caption="Behaviour">行为</Ruby>
|
||||
</VQuote>
|
||||
</li>
|
||||
<li>
|
||||
找到
|
||||
<VQuote>
|
||||
<Ruby caption="Advanced">高级</Ruby>
|
||||
</VQuote>
|
||||
,勾选
|
||||
<VQuote>
|
||||
<Ruby caption="Root Explorer">根目录浏览器</Ruby>
|
||||
</VQuote>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<Header5 className="[&]:mt-0 [&]:pt-0">MT 管理器</Header5>
|
||||
<ul className="ml-2 list-disc list-inside">
|
||||
<li>
|
||||
<div className="inline-flex items-center gap-1">
|
||||
点触主界面左上角的 <FiMenu /> 打开侧边栏
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="inline-flex items-center">
|
||||
点触侧边栏右上方的 <FiMoreVertical className="ml-1" />
|
||||
,点触<VQuote>设置</VQuote>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
勾选<VQuote>请求 Root 权限</VQuote>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export type ExtLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
|
||||
export function ExtLink({ className, icon = true, children, ...props }: ExtLinkProps) {
|
||||
return (
|
||||
<a rel="noreferrer noopener nofollow" target="_blank" className={`link ${className}`} {...props}>
|
||||
<a rel="noreferrer noopener nofollow" target="_blank" className={`link ${className}`} {...props}>
|
||||
{children}
|
||||
{icon && <FiExternalLink className="inline size-sm ml-1" />}
|
||||
</a>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export function FilePathBlock({ children }: { children: React.ReactNode }) {
|
||||
export function FilePathBlock({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<pre className="whitespace-pre-wrap break-all">
|
||||
<code>{children}</code>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Heading } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
export interface HeaderProps {
|
||||
@@ -9,34 +8,24 @@ export interface HeaderProps {
|
||||
|
||||
export function Header3({ children, className, id }: HeaderProps) {
|
||||
return (
|
||||
<Heading
|
||||
as="h3"
|
||||
id={id}
|
||||
className={className}
|
||||
pt={3}
|
||||
pb={1}
|
||||
borderBottom={'1px solid'}
|
||||
borderColor="gray.300"
|
||||
color="gray.800"
|
||||
size="lg"
|
||||
>
|
||||
<h3 id={id} className={`text-2xl pt-3 pb-1 font-bold border-b border-base-300 text-neutral-800 ${className}`}>
|
||||
{children}
|
||||
</Heading>
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
export function Header4({ children, className, id }: HeaderProps) {
|
||||
return (
|
||||
<Heading as="h4" id={id} className={className} pt={3} pb={1} color="gray.700" size="md">
|
||||
<h4 id={id} className={`text-xl pt-3 pb-1 font-semibold text-neutral-800 ${className}`}>
|
||||
{children}
|
||||
</Heading>
|
||||
</h4>
|
||||
);
|
||||
}
|
||||
|
||||
export function Header5({ children, className, id }: HeaderProps) {
|
||||
return (
|
||||
<Heading as="h5" id={id} className={className} pt={3} pb={1} color="gray.700" size="sm">
|
||||
<h5 id={id} className={`text-lg pt-3 pb-1 font-semibold text-neutral-800 ${className}`}>
|
||||
{children}
|
||||
</Heading>
|
||||
</h5>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import { Mark } from '@chakra-ui/react';
|
||||
|
||||
export function HiWord({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Mark bg="orange.100" borderRadius={5} px={2} mx={1}>
|
||||
{children}
|
||||
</Mark>
|
||||
);
|
||||
export function HiWord({ className = '', children }: { className?: string; children: React.ReactNode }) {
|
||||
return <mark className={`bg-orange-100 rounded-md px-2 mx-1 ${className}`}>{children}</mark>;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { chakra, css } from '@chakra-ui/react';
|
||||
|
||||
const cssUnselectable = css({ pointerEvents: 'none', userSelect: 'none' });
|
||||
|
||||
export function VQuote({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<chakra.span css={cssUnselectable}>「</chakra.span>
|
||||
<span className="select-none">「</span>
|
||||
{children}
|
||||
<chakra.span css={cssUnselectable}>」</chakra.span>
|
||||
<span className="select-none">」</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,21 +13,18 @@ export interface InstructionsTabsProps {
|
||||
export function InstructionsTabs({ tabs }: InstructionsTabsProps) {
|
||||
const id = useId();
|
||||
return (
|
||||
<div className="tabs tabs-lift h-[20rem] pb-4">
|
||||
<div className="tabs tabs-lift max-h-[32rem] pb-4">
|
||||
{tabs.map(({ id: _tabId, label, content }, index) => (
|
||||
<Fragment key={_tabId}>
|
||||
<label className="tab">
|
||||
<input type="radio" name={id} defaultChecked={index === 0} />
|
||||
{label}
|
||||
</label>
|
||||
<div className="tab-content border-base-300 bg-base-100 px-4 py-2 overflow-y-auto">{content}</div>
|
||||
<div className="tab-content border-base-300 bg-base-100 px-4 py-2 overflow-y-auto max-h-[30rem]">
|
||||
{content}
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
{/*<label className="tab">*/}
|
||||
{/* <input type="radio" name={id} />a*/}
|
||||
{/*</label>*/}
|
||||
{/*<div className="tab-content border-base-300 bg-base-100 px-4 py-2 overflow-y-auto"></div>*/}
|
||||
{/*<input type="radio" name={id} className="tab" aria-label="安卓" defaultChecked />*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Icon, Kbd } from '@chakra-ui/react';
|
||||
import { BsCommand } from 'react-icons/bs';
|
||||
import { Ruby } from '../Ruby';
|
||||
|
||||
export function MacCommandKey() {
|
||||
return (
|
||||
<ruby>
|
||||
<Kbd>
|
||||
<Icon as={BsCommand} />
|
||||
</Kbd>
|
||||
<rp> (</rp>
|
||||
<rt>command</rt>
|
||||
<rp>)</rp>
|
||||
</ruby>
|
||||
<Ruby caption="command">
|
||||
<kbd className="kbd">
|
||||
<BsCommand className="text-sm" />
|
||||
</kbd>
|
||||
</Ruby>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Icon, Kbd } from '@chakra-ui/react';
|
||||
import { BsShift } from 'react-icons/bs';
|
||||
import { Ruby } from '../Ruby';
|
||||
|
||||
export function ShiftKey() {
|
||||
return (
|
||||
<ruby>
|
||||
<Kbd>
|
||||
<Icon as={BsShift} />
|
||||
</Kbd>
|
||||
<rp> (</rp>
|
||||
<rt>shift</rt>
|
||||
<rp>)</rp>
|
||||
</ruby>
|
||||
<Ruby caption="shift">
|
||||
<kbd className="kbd">
|
||||
<BsShift className="text-sm" />
|
||||
</kbd>
|
||||
</Ruby>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PiFileAudio } from 'react-icons/pi';
|
||||
import { MdDelete, MdVpnKey } from 'react-icons/md';
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface KeyInputProps {
|
||||
sequence: number;
|
||||
@@ -12,24 +12,34 @@ export interface KeyInputProps {
|
||||
onSetValue: (value: string) => void;
|
||||
onDelete: () => void;
|
||||
|
||||
nameLabel?: React.ReactNode;
|
||||
valueLabel?: React.ReactNode;
|
||||
quality?: string;
|
||||
onSetQuality?: (quality: string) => void;
|
||||
|
||||
nameLabel?: ReactNode;
|
||||
valueLabel?: ReactNode;
|
||||
qualityLabel?: ReactNode;
|
||||
|
||||
namePlaceholder?: string;
|
||||
valuePlaceholder?: string;
|
||||
qualityPlaceholder?: string;
|
||||
}
|
||||
|
||||
export function KeyInput(props: KeyInputProps) {
|
||||
const {
|
||||
nameLabel,
|
||||
valueLabel,
|
||||
qualityLabel,
|
||||
namePlaceholder,
|
||||
qualityPlaceholder,
|
||||
valuePlaceholder,
|
||||
sequence,
|
||||
name,
|
||||
quality,
|
||||
value,
|
||||
onSetName,
|
||||
onSetValue,
|
||||
onDelete,
|
||||
onSetQuality,
|
||||
isValidKey,
|
||||
} = props;
|
||||
|
||||
@@ -40,22 +50,39 @@ export function KeyInput(props: KeyInputProps) {
|
||||
</div>
|
||||
|
||||
<div className="join join-vertical flex-1">
|
||||
<label className="input w-full rounded-tl-md rounded-tr-md">
|
||||
<span className="cucursor-default inline-flex items-center gap-1 select-none">
|
||||
{nameLabel || (
|
||||
<>
|
||||
文件 <PiFileAudio />
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="font-mono"
|
||||
placeholder={namePlaceholder}
|
||||
value={name}
|
||||
onChange={(e) => onSetName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<div className="flex">
|
||||
<label className="input w-full rounded-tl-md last:rounded-tr-md">
|
||||
<span className="cucursor-default inline-flex items-center gap-1 select-none">
|
||||
{nameLabel || (
|
||||
<>
|
||||
文件 <PiFileAudio />
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="font-mono"
|
||||
placeholder={namePlaceholder}
|
||||
value={name}
|
||||
onChange={(e) => onSetName(e.target.value)}
|
||||
data-name="key-input--name"
|
||||
/>
|
||||
</label>
|
||||
{onSetQuality && (
|
||||
<label className="input min-w-0 max-w-[10rem] ml-[-1px] rounded-tr-md">
|
||||
<span className="cucursor-default inline-flex items-center gap-1 select-none">
|
||||
{qualityLabel || '音质'}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="font-mono"
|
||||
placeholder={qualityPlaceholder}
|
||||
value={quality}
|
||||
onChange={(e) => onSetQuality(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
<label className="input w-full rounded-bl-md rounded-br-md mt-[-1px]">
|
||||
<span className="cursor-default inline-flex items-center gap-1 select-none">
|
||||
{valueLabel || (
|
||||
|
||||
21
src/components/KeyListContainer.tsx
Normal file
21
src/components/KeyListContainer.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { ReactNode, RefObject } from 'react';
|
||||
|
||||
export interface KeyListContainerProps {
|
||||
keys: unknown[];
|
||||
children?: ReactNode;
|
||||
ref?: RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export function KeyListContainer({ keys, children, ref }: KeyListContainerProps) {
|
||||
const count = keys.length;
|
||||
return (
|
||||
<div ref={ref} className="flex grow min-h-0 overflow-auto pr-4 pt-3">
|
||||
{count > 0 && (
|
||||
<ul className="list bg-base-100 rounded-box shadow-md border border-base-300 w-full min-h-0 max-h-[30rem] overflow-auto">
|
||||
{children}
|
||||
</ul>
|
||||
)}
|
||||
{count === 0 && <p>还没有添加密钥。</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from '@chakra-ui/react';
|
||||
import { ExtLink } from './ExtLink';
|
||||
|
||||
export interface ProjectIssueProps {
|
||||
id: number | string;
|
||||
@@ -7,9 +7,9 @@ export interface ProjectIssueProps {
|
||||
|
||||
export function ProjectIssue({ id, title }: ProjectIssueProps) {
|
||||
return (
|
||||
<Link isExternal target="_blank" href={`https://git.unlock-music.dev/um/um-react/issues/${id}`}>
|
||||
<ExtLink target="_blank" href={`https://git.unlock-music.dev/um/um-react/issues/${id}`}>
|
||||
{`#${id}`}
|
||||
{title && ` - ${title}`}
|
||||
</Link>
|
||||
</ExtLink>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user