From cbfbbab628860688d91548ba0737f6f85ee39e9a Mon Sep 17 00:00:00 2001 From: Wendy Yuchen Sun Date: Sun, 5 Apr 2026 16:15:16 +0800 Subject: [PATCH 1/3] feat: add link input modal --- public/locales/en/link-input-modal.json | 15 +++ public/locales/zh-TW/link-input-modal.json | 15 +++ src/components/edit-icons-tab.js | 22 ++++ src/components/link-input-modal.js | 123 +++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 public/locales/en/link-input-modal.json create mode 100644 public/locales/zh-TW/link-input-modal.json create mode 100644 src/components/link-input-modal.js diff --git a/public/locales/en/link-input-modal.json b/public/locales/en/link-input-modal.json new file mode 100644 index 0000000..9928583 --- /dev/null +++ b/public/locales/en/link-input-modal.json @@ -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" +} diff --git a/public/locales/zh-TW/link-input-modal.json b/public/locales/zh-TW/link-input-modal.json new file mode 100644 index 0000000..80bb129 --- /dev/null +++ b/public/locales/zh-TW/link-input-modal.json @@ -0,0 +1,15 @@ +{ + "title": "插入文字連結", + "display": "文字連結標題", + "displayPlaceholder": "請輸入文字連結標題", + "linkTitle": "文字連結說明", + "optional": "(選填)", + "titlePlaceholder": "請輸入文字連結導向目標的描述", + "url": "文字連結 URL", + "urlPlaceholder": "請輸入點擊文字連結後打開的網址", + "openMethod": "連結開啟方式", + "newTab": "在新分頁中開啟", + "currentTab": "在當前視窗中開啟", + "cancel": "取消", + "confirm": "確認" +} diff --git a/src/components/edit-icons-tab.js b/src/components/edit-icons-tab.js index 71cb8f7..957049d 100644 --- a/src/components/edit-icons-tab.js +++ b/src/components/edit-icons-tab.js @@ -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) => { @@ -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(); @@ -129,6 +142,10 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => { setIsImageModalOpen(true); return; } + if (tab.id === 'create_link') { + setIsLinkModalOpen(true); + return; + } insertLatex(tab); }} > @@ -144,6 +161,11 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => { onClose={() => setIsImageModalOpen(false)} onConfirm={handleImageConfirm} /> + setIsLinkModalOpen(false)} + onConfirm={handleLinkConfirm} + /> ); }; diff --git a/src/components/link-input-modal.js b/src/components/link-input-modal.js new file mode 100644 index 0000000..9383a22 --- /dev/null +++ b/src/components/link-input-modal.js @@ -0,0 +1,123 @@ +import React, { useState, useCallback } 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 = useCallback(() => { + setDisplay(''); + setTitle(''); + setUrl(''); + setOpenInNewTab(true); + }, []); + + const handleClose = useCallback(() => { + resetForm(); + onClose(); + }, [resetForm, onClose]); + + const handleConfirm = useCallback(() => { + 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(); + }, [display, title, url, openInNewTab, onConfirm, handleClose]); + + return ( + +
+
+ + 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" + /> +
+
+ + 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" + /> +
+
+ + 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" + /> +
+
+ {t('openMethod')} +
+ + +
+
+
+
+ ); +}; + +LinkInputModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, +}; + +export default LinkInputModal; From 1b002cc644fd4986bccce4a80e94527f9a191175 Mon Sep 17 00:00:00 2001 From: Wendy Yuchen Sun Date: Sun, 5 Apr 2026 16:39:27 +0800 Subject: [PATCH 2/3] fix: lint --- src/components/link-input-modal.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/link-input-modal.js b/src/components/link-input-modal.js index 9383a22..a7382d8 100644 --- a/src/components/link-input-modal.js +++ b/src/components/link-input-modal.js @@ -57,7 +57,10 @@ const LinkInputModal = ({ isOpen, onClose, onConfirm }) => { />
-