Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions public/locales/en/link-input-modal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "Insert Link",
"display": "Link Display Text",
"displayPlaceholder": "Enter link display text",
"linkTitle": "Link Description",
"optional": "(Optional)",
"titlePlaceholder": "Enter a description for the link target",
"url": "Link URL",
"urlPlaceholder": "Enter the URL to open when clicking the link",
"openMethod": "Link Opening Method",
"newTab": "Open in new tab",
"currentTab": "Open in current window",
"cancel": "Cancel",
"confirm": "Confirm"
}
15 changes: 15 additions & 0 deletions public/locales/zh-TW/link-input-modal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "插入文字連結",
"display": "文字連結標題",
"displayPlaceholder": "請輸入文字連結標題",
"linkTitle": "文字連結說明",
"optional": "(選填)",
"titlePlaceholder": "請輸入文字連結導向目標的描述",
"url": "文字連結 URL",
"urlPlaceholder": "請輸入點擊文字連結後打開的網址",
"openMethod": "連結開啟方式",
"newTab": "在新分頁中開啟",
"currentTab": "在當前視窗中開啟",
"cancel": "取消",
"confirm": "確認"
}
22 changes: 22 additions & 0 deletions src/components/edit-icons-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import markdowns from '@/lib/tabs/markdowns';
import { compare } from '@/lib/data-process';
import mathTabList from '@/lib/tabs/math';
import ImageUploadModal from './image-upload-modal';
import LinkInputModal from './link-input-modal';
import Tooltip from './core/tooltip';

const generateUniqueId = (length = 8) => {
Expand All @@ -22,9 +23,21 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => {
const [selectedMainTabIndex, setSelectedMainTabIndex] = useState(0);
const [selectedMathTabIndex, setSelectedMathTabIndex] = useState(0);
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);

const t = useTranslation('tabs');

const handleLinkConfirm = useCallback(
(markdown) => {
insertLatex({
id: 'create_link',
latex: markdown,
offset: 0,
});
},
[insertLatex]
);

const handleImageConfirm = useCallback(
(file, altText) => {
const fileID = generateUniqueId();
Expand Down Expand Up @@ -129,6 +142,10 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => {
setIsImageModalOpen(true);
return;
}
if (tab.id === 'create_link') {
setIsLinkModalOpen(true);
return;
}
insertLatex(tab);
}}
>
Expand All @@ -144,6 +161,11 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => {
onClose={() => setIsImageModalOpen(false)}
onConfirm={handleImageConfirm}
/>
<LinkInputModal
isOpen={isLinkModalOpen}
onClose={() => setIsLinkModalOpen(false)}
onConfirm={handleLinkConfirm}
/>
</>
);
};
Expand Down
126 changes: 126 additions & 0 deletions src/components/link-input-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from '@/lib/i18n';
import BasicModal from '@/components/core/modal/basic-modal';

const LinkInputModal = ({ isOpen, onClose, onConfirm }) => {
const [display, setDisplay] = useState('');
const [title, setTitle] = useState('');
const [url, setUrl] = useState('');
const [openInNewTab, setOpenInNewTab] = useState(true);
const t = useTranslation('link-input-modal');

const resetForm = () => {
setDisplay('');
setTitle('');
setUrl('');
setOpenInNewTab(true);
};

const handleClose = () => {
resetForm();
onClose();
};

const handleConfirm = () => {
if (!display.trim() || !url.trim()) return;
const prefix = openInNewTab ? '@' : '';
const titlePart = title.trim() ? `[[${title.trim()}]]` : '';
const markdown = `${prefix}[${display.trim()}]${titlePart}(${url.trim()})`;
onConfirm(markdown);
handleClose();
};

return (
<BasicModal
title={t('title')}
isOpen={isOpen}
onClose={handleClose}
onCancel={handleClose}
onConfirm={handleConfirm}
cancelLabel={t('cancel')}
confirmLabel={t('confirm')}
>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<label className="text-text-primary text-base" htmlFor="link-display">
{t('display')}
</label>
<input
id="link-display"
type="text"
value={display}
onChange={(e) => setDisplay(e.target.value)}
placeholder={t('displayPlaceholder')}
className="w-full border border-border-main rounded-lg px-4 py-3 text-base text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-primary"
aria-required="true"
/>
</div>
<div className="flex flex-col gap-2">
<label
className="flex gap-2 items-center text-text-primary text-base"
htmlFor="link-title"
>
<span>{t('linkTitle')}</span>
<span className="text-text-primary">{t('optional')}</span>
</label>
<input
id="link-title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder={t('titlePlaceholder')}
className="w-full border border-border-main rounded-lg px-4 py-3 text-base text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-text-primary text-base" htmlFor="link-url">
{t('url')}
</label>
<input
id="link-url"
type="text"
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder={t('urlPlaceholder')}
className="w-full border border-border-main rounded-lg px-4 py-3 text-base text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-primary"
aria-required="true"
/>
</div>
<div className="flex flex-col gap-2">
<span className="text-text-primary text-base">{t('openMethod')}</span>
<div className="flex items-center">
<label className="flex flex-1 items-center gap-3 py-1 cursor-pointer">
<input
type="radio"
name="link-open-method"
checked={openInNewTab}
onChange={() => setOpenInNewTab(true)}
className="accent-primary w-5 h-5 shrink-0"
/>
<span className="text-base text-text-primary">{t('newTab')}</span>
</label>
<label className="flex flex-1 items-center gap-3 px-2 py-1 cursor-pointer">
<input
type="radio"
name="link-open-method"
checked={!openInNewTab}
onChange={() => setOpenInNewTab(false)}
className="accent-primary w-5 h-5 shrink-0"
/>
<span className="text-base text-text-primary">{t('currentTab')}</span>
</label>
</div>
</div>
</div>
</BasicModal>
);
};

LinkInputModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
};

export default LinkInputModal;
Loading