Skip to content

Commit 64a013f

Browse files
feat: update a8m layout for v2 design (#113)
* refactor: move Header into Home, add A8M logo, replace headlessui menus - Add @tabler/icons-react dependency - Add A8M logo SVG asset - Migrate header menus from headlessui to shared DropdownMenu component, replace heroicons with tabler icons, add title input and action buttons - Remove Header from shared Layout so each page owns its header - Update Home to render Header directly with import/export callbacks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> s * refactor: update left panel edit area styling and CodeMirror theme - Replace ToggleButtonGroup with SegmentedControl for latex delimiter - Update left panel background and padding (cyanLight → blue-50) - Add border/bg/rounded container for the editor area - Expand CodeMirror theme with gutter, line number, and content styles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: update right panel preview area styling and controls - Replace documentFormat/documentColor ToggleButtonGroups with a single DropdownMenu for document color - Update preview container classes (border-2 → border, add leading/space-y, bg-black → bg-gray-800, text-black → text-text-primary) - Adjust right panel padding to p-6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: update modal styles, icons, and sizing - Use tabler icon components for delimiter symbols in ConvertHintModal - Set size="sm" on ConvertHintModal and TipModal - Remove fixed-width wrapper div in SettingModal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: narrow header props to title/onTitleChange and add propTypes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0135591 commit 64a013f

File tree

12 files changed

+395
-464
lines changed

12 files changed

+395
-464
lines changed

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@coseeing/see-mark": "^1.5.0",
1010
"@headlessui/react": "^1.7.17",
1111
"@heroicons/react": "^2.0.18",
12+
"@tabler/icons-react": "^3.40.0",
1213
"classnames": "^2.3.2",
1314
"codemirror": "^6.0.1",
1415
"core-js": "^3.21.1",

src/components/edit-icons-tab.js

Lines changed: 98 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -40,136 +40,111 @@ const EditIconsTab = ({ insertLatex, addImageToExport }) => {
4040
);
4141

4242
return (
43-
<div className="flex h-[600px]">
43+
<>
4444
<Tab.Group
4545
as="div"
4646
selectedIndex={selectedMainTabIndex}
4747
onChange={setSelectedMainTabIndex}
48-
className="flex flex-col w-full"
48+
className="flex flex-col w-full h-[600px] border-r border-border-main"
4949
>
50-
<div className="flex bg-cyan p-2">
51-
<Tab.List as="div" className="w-full flex bg-cyan">
52-
{mainTabList.map(({ id }, index) => (
53-
<Tab
54-
as="button"
55-
key={id}
56-
className={`rounded flex-1 px-4 py-2 text-sm text-center cursor-pointer transition-colors ${
57-
selectedMainTabIndex === index ? 'bg-white text-black' : 'bg-cyan text-white'
58-
}`}
59-
>
60-
{t(`main.${id}`)}
61-
</Tab>
62-
))}
63-
</Tab.List>
64-
</div>
65-
<div className="flex flex-1 h-full">
66-
<Tab.Panels as="div" className="flex w-full">
67-
<Tab.Panel className="h-full w-full">
68-
<Tab.Group
69-
as="div"
70-
selectedIndex={selectedMathTabIndex}
71-
onChange={setSelectedMathTabIndex}
72-
className="h-full flex"
73-
>
74-
<div className="flex h-full w-full">
75-
<Tab.List
76-
as="div"
77-
className="flex flex-col bg-cyan p-2"
78-
style={{
79-
maxHeight: '550px',
80-
overflowY: 'auto',
81-
scrollbarWidth: 'none', // hide scrollbar in Firefox
82-
msOverflowStyle: 'none', // hide scrollbar in IE/Edge
83-
}}
84-
>
85-
{mathTabList.map((tab, mathTabIndex) => (
86-
<Tooltip key={tab.id} label={t(`categorys.${tab.id}`)} position="right">
87-
<Tab
88-
as="button"
89-
key={tab.id}
90-
aria-label={t(`categorys.${tab.id}`)}
91-
className={`group relative rounded mb-1 category-icon h-12 w-12 flex items-center justify-center mx-0.5 bg-white cursor-pointer transition-colors ${
92-
selectedMathTabIndex === mathTabIndex ? 'active' : ''
93-
}`}
94-
>
95-
<tab.Icon width={48} height={48} />
96-
{selectedMathTabIndex === mathTabIndex && (
97-
// White triangle arrow pointing right to indicate selected tab
98-
<div
99-
className="absolute -right-2 top-1/2 -translate-y-1/2 w-0 h-0"
100-
style={{
101-
borderTop: '8px solid transparent',
102-
borderBottom: '8px solid transparent',
103-
borderLeft: '8px solid white',
104-
}}
105-
/>
106-
)}
107-
</Tab>
108-
</Tooltip>
109-
))}
110-
</Tab.List>
111-
<Tab.Panels
112-
as="div"
113-
className="flex-1 bg-gray-50 border border-gray-300 p-2 overflow-y-auto"
114-
>
115-
{mathTabList.map((mathTab) => {
116-
return (
117-
<Tab.Panel key={mathTab.id} className="flex flex-wrap">
118-
{(mathTab?.subTabs || []).sort(compare('order', 'asc')).map((subTab) => (
119-
<Tooltip
120-
key={subTab.id}
121-
label={t(`latexs.${subTab.id}`)}
122-
position="top"
50+
<Tab.List as="div" className="w-full flex items-center border-b border-border-main">
51+
{mainTabList.map(({ id }, index) => (
52+
<Tab
53+
as="button"
54+
key={id}
55+
className={`rounded-lg flex-1 text-sm text-center leading-[1.5] py-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary m-1 cursor-pointer transition-colors ${
56+
selectedMainTabIndex === index ? 'bg-blue-200 text-primary' : 'text-text-primary'
57+
}`}
58+
>
59+
{t(`main.${id}`)}
60+
</Tab>
61+
))}
62+
</Tab.List>
63+
<Tab.Panels as="div" className="flex flex-1 h-full w-full">
64+
<Tab.Panel className="h-full w-full">
65+
<Tab.Group
66+
as="div"
67+
selectedIndex={selectedMathTabIndex}
68+
onChange={setSelectedMathTabIndex}
69+
className="h-full flex"
70+
>
71+
<div className="flex h-full w-full">
72+
<Tab.List
73+
as="div"
74+
className="flex flex-col px-2 py-3 gap-2 border-r border-border-main"
75+
style={{
76+
maxHeight: '562px',
77+
overflowY: 'auto',
78+
scrollbarWidth: 'none', // hide scrollbar in Firefox
79+
msOverflowStyle: 'none', // hide scrollbar in IE/Edge
80+
}}
81+
>
82+
{mathTabList.map((tab, mathTabIndex) => (
83+
<Tooltip key={tab.id} label={t(`categorys.${tab.id}`)} position="right">
84+
<Tab
85+
as="button"
86+
key={tab.id}
87+
aria-label={t(`categorys.${tab.id}`)}
88+
className={`group relative rounded category-icon flex items-center justify-center cursor-pointer transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ${
89+
selectedMathTabIndex === mathTabIndex
90+
? 'active bg-blue-100'
91+
: 'bg-white hover:bg-blue-100'
92+
}`}
93+
>
94+
<tab.Icon width={52} height={52} />
95+
</Tab>
96+
</Tooltip>
97+
))}
98+
</Tab.List>
99+
<Tab.Panels as="div" className="flex-1 px-2 py-3 overflow-y-auto">
100+
{mathTabList.map((mathTab) => {
101+
return (
102+
<Tab.Panel key={mathTab.id} className="flex flex-wrap gap-2">
103+
{(mathTab?.subTabs || []).sort(compare('order', 'asc')).map((subTab) => (
104+
<Tooltip key={subTab.id} label={t(`latexs.${subTab.id}`)} position="top">
105+
<button
106+
aria-label={t(`latexs.${subTab.id}`)}
107+
className="group relative bg-white rounded border border-gray-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
108+
onClick={() => insertLatex(subTab)}
123109
>
124-
<button
125-
aria-label={t(`latexs.${subTab.id}`)}
126-
className="w-w5 h-w5 group relative m-1"
127-
onClick={() => insertLatex(subTab)}
128-
>
129-
<subTab.Icon
130-
width={50}
131-
height={50}
132-
className="bg-cyanLight rounded"
133-
/>
134-
</button>
135-
</Tooltip>
136-
))}
137-
</Tab.Panel>
138-
);
139-
})}
140-
</Tab.Panels>
141-
</div>
142-
</Tab.Group>
143-
</Tab.Panel>
144-
<Tab.Panel className="flex flex-wrap content-baseline bg-cyan p-1 h-full">
145-
{markdowns.map((tab) => (
146-
<Tooltip key={tab.id} label={t(`markdown.${tab.id}`)} position="top">
147-
<button
148-
aria-label={t(`markdown.${tab.id}`)}
149-
className="group relative rounded mb-1 h-12 w-12 mx-0.5 bg-white cursor-pointer transition-colors flex items-center justify-center"
150-
onClick={() => {
151-
if (tab.id === 'insert_image') {
152-
setIsImageModalOpen(true);
153-
return;
154-
}
155-
insertLatex(tab);
156-
}}
157-
>
158-
<tab.Icon width={48} height={48} />
159-
</button>
160-
</Tooltip>
161-
))}
162-
</Tab.Panel>
163-
</Tab.Panels>
164-
</div>
165-
166-
<ImageUploadModal
167-
isOpen={isImageModalOpen}
168-
onClose={() => setIsImageModalOpen(false)}
169-
onConfirm={handleImageConfirm}
170-
/>
110+
<subTab.Icon width={50} height={50} className="rounded" />
111+
</button>
112+
</Tooltip>
113+
))}
114+
</Tab.Panel>
115+
);
116+
})}
117+
</Tab.Panels>
118+
</div>
119+
</Tab.Group>
120+
</Tab.Panel>
121+
<Tab.Panel className="flex flex-wrap gap-2 content-baseline px-2 py-3 h-full">
122+
{markdowns.map((tab) => (
123+
<Tooltip key={tab.id} label={t(`markdown.${tab.id}`)} position="top">
124+
<button
125+
aria-label={t(`markdown.${tab.id}`)}
126+
className="group relative rounded bg-white cursor-pointer border border-gray-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary flex items-center justify-center"
127+
onClick={() => {
128+
if (tab.id === 'insert_image') {
129+
setIsImageModalOpen(true);
130+
return;
131+
}
132+
insertLatex(tab);
133+
}}
134+
>
135+
<tab.Icon width={50} height={50} />
136+
</button>
137+
</Tooltip>
138+
))}
139+
</Tab.Panel>
140+
</Tab.Panels>
171141
</Tab.Group>
172-
</div>
142+
<ImageUploadModal
143+
isOpen={isImageModalOpen}
144+
onClose={() => setIsImageModalOpen(false)}
145+
onConfirm={handleImageConfirm}
146+
/>
147+
</>
173148
);
174149
};
175150

src/components/header/index.js

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
11
import React, { useState } from 'react';
2+
import PropTypes from 'prop-types';
23

4+
import { ReactComponent as A8mLogo } from '@/components/svg/a8m-logo.svg';
35
import Menu from './menu';
46
import LanguageMenu from './language-menu';
57
import TipModal from '@/components/home/tip-modal';
6-
import { ReactComponent as QuestionCircleComponent } from '@/components/svg/question-circle.svg';
8+
import { IconBulb } from '@tabler/icons-react';
79
import { useTranslation } from '@/lib/i18n';
10+
import Button from '@/components/core/button';
811

9-
const Header = () => {
12+
const Header = ({ onImportClick, onExportClick, title, onTitleChange }) => {
1013
const t = useTranslation('home');
1114
const [showTipModal, setShowTipModal] = useState(false);
1215

1316
return (
14-
<header className="px-8 md:px-20 fixed h-20 flex justify-between items-center bg-white text-md md:text-2xl font-bold inset-x-0 z-10">
15-
<div className="flex">
16-
<h1 className="m-0">Access8Math</h1>
17-
<button
18-
className="hover:scale-110 transition-scale ml-2"
17+
<header className="px-6 fixed h-[72px] flex items-center gap-2 bg-white inset-x-0 z-10 shadow-shadow2">
18+
<div className="flex items-center gap-3 grow">
19+
<h1 className="sr-only">Access8Math</h1>
20+
<A8mLogo aria-hidden="true" />
21+
<input
22+
value={title}
23+
type="text"
24+
style={{ outline: 'none' }}
25+
className="grow max-w-[280px] text-text-primary placeholder-text-placeholder text-xl font-medium leading-[1.4] pb-2 border-b-2 border-primary"
26+
placeholder={t('pleaseInputTitle')}
27+
aria-label={t('pleaseInputTitle')}
28+
onChange={(e) => onTitleChange(e.target.value)}
29+
/>
30+
</div>
31+
<div className="flex items-center gap-3">
32+
<Button
33+
variant="tertiary"
34+
className="min-w-[88px] flex items-center gap-1"
1935
onClick={() => setShowTipModal(true)}
20-
aria-label={t('descript')}
2136
>
22-
<QuestionCircleComponent className="w-5 h-5" />
23-
</button>
24-
</div>
25-
<div className="flex items-center">
26-
<div className="md:mr-12 mr-8">
27-
<LanguageMenu />
28-
</div>
37+
<IconBulb size={16} aria-hidden="true" />
38+
<span>{t('instructions')}</span>
39+
</Button>
2940
<Menu />
41+
<LanguageMenu />
42+
<Button variant="secondary" className="min-w-[88px]" onClick={onImportClick}>
43+
{t('import')}
44+
</Button>
45+
<Button variant="primary" className="min-w-[88px]" onClick={onExportClick}>
46+
{t('export')}
47+
</Button>
3048
</div>
3149
<TipModal isOpen={showTipModal} onClose={() => setShowTipModal(false)} />
3250
</header>
3351
);
3452
};
3553

54+
Header.propTypes = {
55+
onImportClick: PropTypes.func.isRequired,
56+
onExportClick: PropTypes.func.isRequired,
57+
title: PropTypes.string.isRequired,
58+
onTitleChange: PropTypes.func.isRequired,
59+
};
60+
3661
export default Header;

0 commit comments

Comments
 (0)