fix: some style
@ -1,5 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@appflowyinc/client-api-wasm':
|
||||
specifier: 0.1.1
|
||||
@ -11662,7 +11666,3 @@ packages:
|
||||
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
@ -5,7 +5,7 @@ const cheerio = require('cheerio');
|
||||
const { fetch } = require('bun');
|
||||
const distDir = path.join(__dirname, 'dist');
|
||||
const indexPath = path.join(distDir, 'index.html');
|
||||
|
||||
const logo = path.join(distDir, 'appflowy.svg');
|
||||
const setOrUpdateMetaTag = ($, selector, attribute, content) => {
|
||||
if ($(selector).length === 0) {
|
||||
$('head').append(`<meta ${attribute}="${selector.match(/\[(.*?)\]/)[1]}" content="${content}">`);
|
||||
@ -88,13 +88,13 @@ const createServer = async (req) => {
|
||||
let htmlData = fs.readFileSync(indexPath, 'utf8');
|
||||
const $ = cheerio.load(htmlData);
|
||||
|
||||
const description = 'Write, share, comment, react, and publish docs quickly and securely on AppFlowy.';
|
||||
const description = 'Write, share, and publish docs quickly on AppFlowy. \n Get started for free.';
|
||||
let title = 'AppFlowy';
|
||||
const url = 'https://appflowy.com';
|
||||
let image = 'https://d3uafhn8yrvdfn.cloudfront.net/website/production/_next/static/media/og-image.e347bfb5.png';
|
||||
let image = logo;
|
||||
// Inject meta data into the HTML to support SEO and social sharing
|
||||
if (metaData) {
|
||||
title = metaData.view.name;
|
||||
title = `${metaData.view.name} | AppFlowy`;
|
||||
|
||||
try {
|
||||
const cover = metaData.view.extra ? JSON.parse(metaData.view.extra)?.cover : null;
|
||||
|
25
frontend/appflowy_web_app/src/assets/appflowy.svg
Normal file
@ -0,0 +1,25 @@
|
||||
<svg width="57" height="14" viewBox="0 0 57 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1171274570">
|
||||
<path id="Vector"
|
||||
d="M8.35547 4.01126H10.1226V4.67123C10.5568 4.21099 11.3679 3.84192 12.2589 3.84192C14.1641 3.84192 15.3938 5.31819 15.3938 7.36065C15.3938 9.46563 13.9488 11.0947 11.7361 11.0947C11.1213 11.0947 10.5204 10.9871 10.1226 10.7569V13.9995H8.35547V4.01126ZM10.1226 6.25258V9.04967C10.5994 9.35708 10.9989 9.44913 11.5824 9.44913C12.8121 9.44913 13.5189 8.55729 13.5189 7.40494C13.5189 6.2986 12.8737 5.49968 11.7057 5.49968C11.1065 5.50142 10.5534 5.77844 10.1226 6.25258Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_2"
|
||||
d="M3.98189 3.88086C1.76836 3.88086 0.324219 5.50996 0.324219 7.61495C0.324219 9.65915 1.55386 11.1345 3.45912 11.1345C4.35009 11.1345 5.16464 10.7655 5.59536 10.3035V10.9652H7.36862V5.16348C6.05561 4.16917 4.60019 3.88086 3.98189 3.88086ZM5.59536 8.72215C5.16117 9.19716 4.61147 9.47418 4.01228 9.47418C2.84516 9.47418 2.19908 8.67526 2.19908 7.56979C2.19908 6.41657 2.90595 5.52473 4.13473 5.52473C4.71916 5.52473 5.11861 5.61765 5.59536 5.92506V8.72215Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_3"
|
||||
d="M16.1484 4.01126H17.9156V4.67123C18.3498 4.21099 19.16 3.84192 20.051 3.84192C21.9571 3.84192 23.1859 5.31819 23.1859 7.36065C23.1859 9.46563 21.7418 11.0947 19.5291 11.0947C18.9143 11.0947 18.3133 10.9871 17.9156 10.7569V13.9995H16.1484V4.01126ZM17.9156 6.25258V9.04967C18.3915 9.35708 18.791 9.44913 19.3754 9.44913C20.6042 9.44913 21.311 8.55729 21.311 7.40494C21.311 6.2986 20.6658 5.49968 19.4978 5.49968C18.8986 5.50142 18.3455 5.77844 17.9156 6.25258Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_4"
|
||||
d="M28.3102 2.02887C27.9904 1.80209 27.6113 1.6738 27.2195 1.6598C26.4206 1.6598 26.0037 2.0749 26.0037 3.02752V4.01141H27.2481V5.67091H26.0055V10.9264H24.2383V2.88945C24.2383 0.968563 25.1605 0.000305176 26.6967 0.000305176C27.4036 0.000305176 28.0497 0.173984 28.5099 0.461422H30.0774V8.29867C30.0774 9.08022 30.2771 9.41976 30.6766 9.41976C30.9692 9.41976 31.1976 9.29732 31.4147 9.14361L31.7829 10.4957C31.3834 10.8187 30.7842 11.0793 29.9593 11.0793C28.8989 11.0793 28.3145 10.434 28.3145 9.0203L28.3102 2.02887Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_5"
|
||||
d="M35.5466 3.84192C37.8357 3.84192 39.3111 5.39374 39.3111 7.46833C39.3111 9.54292 37.8348 11.0947 35.5466 11.0947C33.2584 11.0947 31.7812 9.54292 31.7812 7.46833C31.7812 5.39374 33.2566 3.84192 35.5466 3.84192ZM35.5466 9.45087C36.6373 9.45087 37.4362 8.66932 37.4362 7.46833C37.4362 6.30034 36.6225 5.50142 35.5466 5.50142C34.5045 5.50142 33.6717 6.26995 33.6717 7.46833C33.6717 8.62069 34.4707 9.45087 35.5466 9.45087Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_6"
|
||||
d="M48.0544 10.9565H46.4713L44.995 7.11477C44.8874 6.85425 44.8413 6.56161 44.7641 6.30022C44.7077 6.58869 44.6255 6.87148 44.5183 7.14517L43.0594 10.9565H41.5545L39.3105 4.0094H41.201L42.3074 7.77388C42.3943 8.07652 42.4611 8.38461 42.5071 8.69611C42.5772 8.38422 42.6642 8.07635 42.7676 7.77388L44.0581 4.0094H45.6212L46.9672 7.75999C47.0701 8.0738 47.157 8.39262 47.2277 8.71522C47.2893 8.39305 47.3666 8.05437 47.443 7.71657L48.4573 4.01374H50.2401L48.0544 10.9565Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Vector_7"
|
||||
d="M53.9557 10.9265C52.9414 13.3997 52.2962 13.9997 51.2819 13.9997C50.6514 13.9997 50.1903 13.7531 49.7909 13.4613L50.3596 12.1249C50.5437 12.2325 50.8051 12.3706 51.0822 12.3706C51.5276 12.3706 51.8498 12.0172 52.1425 11.2947L52.2962 10.9265L49.0996 4.01147H51.222L52.8354 7.79159C52.9735 8.12939 53.0656 8.45157 53.1732 8.79024C53.2298 8.4465 53.3119 8.10746 53.419 7.77596L54.6643 4.01147H56.6755L53.9557 10.9265Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
6
frontend/appflowy_web_app/src/assets/board.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.2023 19.4658V4.53479C10.2023 3.11729 9.61505 2.55029 8.15599 2.55029H4.4487C2.98964 2.55029 2.40234 3.11729 2.40234 4.53479V19.4658C2.40234 20.8833 2.98964 21.4503 4.4487 21.4503H8.15599C9.61505 21.4503 10.2023 20.8833 10.2023 19.4658Z"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.6022 13.1778V4.59779C21.6022 3.13529 21.015 2.55029 19.5559 2.55029H15.8486C14.3895 2.55029 13.8022 3.13529 13.8022 4.59779V13.1778C13.8022 14.6403 14.3895 15.2253 15.8486 15.2253H19.5559C21.015 15.2253 21.6022 14.6403 21.6022 13.1778Z"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 811 B |
7
frontend/appflowy_web_app/src/assets/calendar.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="calendar-2">
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M6.57344 1.0002C6.57344 0.558368 6.21527 0.200195 5.77344 0.200195C5.33161 0.200195 4.97344 0.558368 4.97344 1.0002V2.01729H4C1.79086 2.01729 0 3.80815 0 6.01729V17.0003C0 19.2095 1.79086 21.0003 4 21.0003H16C18.2091 21.0003 20 19.2095 20 17.0003V6.01728C20 3.80815 18.2091 2.01729 16 2.01729H15.0266V1.0002C15.0266 0.558368 14.6684 0.200195 14.2266 0.200195C13.7847 0.200195 13.4266 0.558368 13.4266 1.0002V2.01729H6.57344V1.0002ZM4.97344 3.61729V4.22053C4.97344 4.66236 5.33161 5.02053 5.77344 5.02053C6.21527 5.02053 6.57344 4.66236 6.57344 4.22053V3.61729H13.4266V4.22053C13.4266 4.66236 13.7847 5.02053 14.2266 5.02053C14.6684 5.02053 15.0266 4.66236 15.0266 4.22053V3.61729H16C17.3255 3.61729 18.4 4.6918 18.4 6.01728V7.81165H1.6V6.01729C1.6 4.6918 2.67452 3.61729 4 3.61729H4.97344ZM1.6 9.41165V17.0003C1.6 18.3258 2.67452 19.4003 4 19.4003H16C17.3255 19.4003 18.4 18.3258 18.4 17.0003V9.41165H1.6Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
10
frontend/appflowy_web_app/src/assets/chat_ai.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_chat-ai_3">
|
||||
<path id="Subtract"
|
||||
d="M13.0581 3.23471C13.0581 2.86764 12.7812 2.55686 12.4144 2.54173C12.2771 2.53606 12.139 2.5332 12.0002 2.5332C6.66255 2.5332 2.3335 6.77042 2.3335 11.9969C2.33228 13.5462 2.71947 15.0712 3.45967 16.4323C3.73517 16.9172 3.89144 17.4763 3.89144 18.0675C3.89144 18.944 3.54989 19.7415 2.99083 20.3456C2.93605 20.4584 2.85711 20.6518 2.85711 20.7533C2.85889 20.929 2.92519 21.098 3.04341 21.2281C3.16163 21.3582 3.32353 21.4404 3.49833 21.4589H12.0002C17.341 21.4589 21.6668 17.2233 21.6668 11.9953C21.6668 11.7828 21.6597 11.5719 21.6456 11.3629C21.6212 11.0017 21.3119 10.7343 20.9499 10.7343C20.5018 10.7343 20.1637 11.1382 20.1882 11.5857C20.1956 11.7215 20.1994 11.858 20.1994 11.9953C20.1994 16.3259 16.4581 19.9449 12.0002 19.9449L4.9831 19.8478C5.24744 19.1672 5.35203 18.4362 5.29853 17.708C5.24502 16.9798 5.09175 16.2727 4.73075 15.638C4.13625 14.5408 3.78447 13.2922 3.78447 11.9969C3.78447 7.66781 7.54383 3.98417 12.0002 3.98417C12.081 3.98417 12.1617 3.98539 12.242 3.98779C12.6757 4.00077 13.0581 3.66856 13.0581 3.23471Z"
|
||||
fill="currentColor"/>
|
||||
<path id="Union"
|
||||
d="M18.2063 2.97527C18.3771 2.5635 18.9604 2.5636 19.131 2.97543L19.3536 3.51281C19.6922 4.33007 20.3415 4.97943 21.1587 5.31811L21.6924 5.53929C22.1043 5.71 22.1042 6.2935 21.6922 6.46406L21.1549 6.68654C20.3368 7.02524 19.6869 7.67521 19.3483 8.49332L19.1286 9.02409C18.958 9.43628 18.3742 9.43617 18.2037 9.02393L17.9825 8.48897C17.6439 7.6702 16.9936 7.0197 16.1749 6.68091L15.6426 6.46061C15.2305 6.29009 15.2304 5.70644 15.6424 5.53577L16.1799 5.31314C16.997 4.97467 17.6463 4.32564 17.9851 3.50867L18.2063 2.97527Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
7
frontend/appflowy_web_app/src/assets/chevron_down.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon_小三角" opacity="0.5">
|
||||
<path id="Vector 31"
|
||||
d="M5.83607 8.09961L3.51977 4.91469C3.30345 4.61725 3.51592 4.20001 3.8837 4.20001H8.5163C8.88408 4.20001 9.09655 4.61725 8.88023 4.91469L6.56393 8.09961C6.38422 8.3467 6.01578 8.3467 5.83607 8.09961Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 480 B |
9
frontend/appflowy_web_app/src/assets/document.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="document">
|
||||
<g id="4">
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M20.1783 8.93656C20.2569 9.17734 20.2969 9.42902 20.2969 9.6823V14.0265C20.2969 16.5387 19.8692 18.5 18.7213 19.8261C17.5476 21.1821 15.7813 21.6972 13.55 21.6972H8.45C6.21866 21.6972 4.45244 21.1821 3.27874 19.8261C2.13079 18.5 1.70312 16.5387 1.70312 14.0265V8.1346C1.70312 5.62238 2.13079 3.66112 3.27874 2.33493C4.45244 0.978988 6.21866 0.463867 8.45 0.463867H12.1073C12.3671 0.463867 12.6249 0.509322 12.869 0.598176C13.2274 0.728622 13.5464 0.948927 13.7952 1.23798L19.7147 8.11373C19.9221 8.35466 20.0797 8.63433 20.1783 8.93656ZM3.29688 8.1346C3.29688 5.73691 3.71921 4.26125 4.48376 3.37799C5.22256 2.52448 6.43134 2.05762 8.45 2.05762H11.8281V5.04936C11.8281 6.55636 12.0326 7.80405 12.7648 8.65464C13.5286 9.54177 14.681 9.7955 16.025 9.7955H18.7031V14.0265C18.7031 16.4242 18.2808 17.8998 17.5162 18.7831C16.7774 19.6366 15.5687 20.1034 13.55 20.1034H8.45C6.43134 20.1034 5.22256 19.6366 4.48376 18.7831C3.71921 17.8998 3.29688 16.4242 3.29688 14.0265V8.1346ZM13.4219 5.04936V3.24708L17.6874 8.20175H16.025C14.819 8.20175 14.2714 7.96182 13.9727 7.61481C13.6424 7.23125 13.4219 6.50431 13.4219 5.04936Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
7
frontend/appflowy_web_app/src/assets/grid.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1686556136">
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M8.58687 1.98039H17.6008C18.7164 1.98039 19.6208 2.88477 19.6208 4.00039V6.06034H8.58687V1.98039ZM8.58687 7.64034H19.6208V12.3601H8.58687V7.64034ZM7.00688 12.3601V7.64034H2.38078V12.3601H7.00688ZM2.38078 13.9401H7.00688V18.0204H4.40078C3.28516 18.0204 2.38078 17.116 2.38078 16.0004V13.9401ZM8.58687 13.9401H19.6208V16.0004C19.6208 17.116 18.7164 18.0204 17.6008 18.0204H8.58687V13.9401ZM7.00688 1.98039V6.06034H2.38078V4.00039C2.38078 2.88477 3.28516 1.98039 4.40078 1.98039H7.00688ZM0.800781 4.00039C0.800781 2.01216 2.41256 0.400391 4.40078 0.400391H17.6008C19.589 0.400391 21.2008 2.01217 21.2008 4.00039V16.0004C21.2008 17.9886 19.589 19.6004 17.6008 19.6004H4.40078C2.41255 19.6004 0.800781 17.9886 0.800781 16.0004V4.00039Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 992 B |
@ -1,11 +1,18 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1321314169">
|
||||
<path id="Vector" d="M7.63281 6.36667L12.8261 1.17334" stroke="currentColor" stroke-width="1.06667"
|
||||
stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M13.331 3.70681V0.666809H10.291" stroke="currentColor" stroke-width="1.06667"
|
||||
stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3"
|
||||
d="M6.36602 0.666809H5.09935C1.93268 0.666809 0.666016 1.93348 0.666016 5.10014V8.90014C0.666016 12.0668 1.93268 13.3335 5.09935 13.3335H8.89935C12.066 13.3335 13.3327 12.0668 13.3327 8.90014V7.63348"
|
||||
stroke="currentColor" stroke-width="1.06667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="message-programming">
|
||||
<g id="vuesax/linear/message-programming">
|
||||
<g id="message-programming_2">
|
||||
<path id="Vector"
|
||||
d="M8.5 18.9697H8C4 18.9697 2 17.9697 2 12.9697V7.96973C2 3.96973 4 1.96973 8 1.96973H16C20 1.96973 22 3.96973 22 7.96973V12.9697C22 16.9697 20 18.9697 16 18.9697H15.5C15.19 18.9697 14.89 19.1198 14.7 19.3698L13.2 21.3698C12.54 22.2498 11.46 22.2498 10.8 21.3698L9.29999 19.3698C9.13999 19.1498 8.78 18.9697 8.5 18.9697Z"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group 1686556182">
|
||||
<path id="Vector_2" d="M12 7V12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M11.9946 15H12.0036" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 836 B After Width: | Height: | Size: 1.1 KiB |
11
frontend/appflowy_web_app/src/assets/search.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="vuesax/linear/search-normal">
|
||||
<g id="search-normal">
|
||||
<path id="Vector"
|
||||
d="M7.66665 13.9999C11.1644 13.9999 14 11.1644 14 7.66659C14 4.16878 11.1644 1.33325 7.66665 1.33325C4.16884 1.33325 1.33331 4.16878 1.33331 7.66659C1.33331 11.1644 4.16884 13.9999 7.66665 13.9999Z"
|
||||
stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M14.6666 14.6666L13.3333 13.3333" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 691 B |
14
frontend/appflowy_web_app/src/assets/side_outlined.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon_wiki-side-fold_outlined">
|
||||
<g id="Union">
|
||||
<path d="M9.33398 2.66669C9.70217 2.66669 10.0007 2.96516 10.0007 3.33335C10.0007 3.70154 9.70217 4.00002 9.33398 4.00002H1.33398C0.965795 4.00002 0.667317 3.70154 0.667317 3.33335C0.667317 2.96516 0.965795 2.66669 1.33398 2.66669H9.33398Z"
|
||||
fill="currentColor"/>
|
||||
<path d="M1.33398 7.33335H9.33398C9.70217 7.33335 10.0007 7.63183 10.0007 8.00002C10.0007 8.36821 9.70217 8.66669 9.33398 8.66669H1.33398C0.965795 8.66669 0.667317 8.36821 0.667317 8.00002C0.667317 7.63183 0.965795 7.33335 1.33398 7.33335Z"
|
||||
fill="currentColor"/>
|
||||
<path d="M10.0007 12.6667C10.0007 12.2985 9.70217 12 9.33398 12H1.33398C0.965795 12 0.667317 12.2985 0.667317 12.6667C0.667317 13.0349 0.965795 13.3334 1.33398 13.3334H9.33398C9.70217 13.3334 10.0007 13.0349 10.0007 12.6667Z"
|
||||
fill="currentColor"/>
|
||||
<path d="M11.5292 5.80476C11.2689 5.54441 11.2689 5.1223 11.5292 4.86195C11.7896 4.6016 12.2117 4.6016 12.4721 4.86195L15.1387 7.52862C15.3991 7.78897 15.3991 8.21108 15.1387 8.47143L12.4721 11.1381C12.2117 11.3984 11.7896 11.3984 11.5292 11.1381C11.2689 10.8777 11.2689 10.4556 11.5292 10.1953L13.7245 8.00002L11.5292 5.80476Z"
|
||||
fill="currentColor"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
14
frontend/appflowy_web_app/src/assets/sun.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="sun">
|
||||
<g id="vuesax/linear/sun">
|
||||
<g id="sun_2">
|
||||
<path id="Vector"
|
||||
d="M12 18.5C15.5899 18.5 18.5 15.5899 18.5 12C18.5 8.41015 15.5899 5.5 12 5.5C8.41015 5.5 5.5 8.41015 5.5 12C5.5 15.5899 8.41015 18.5 12 18.5Z"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2"
|
||||
d="M19.14 19.14L19.01 19.01M19.01 4.99L19.14 4.86L19.01 4.99ZM4.86 19.14L4.99 19.01L4.86 19.14ZM12 2.08V2V2.08ZM12 22V21.92V22ZM2.08 12H2H2.08ZM22 12H21.92H22ZM4.99 4.99L4.86 4.86L4.99 4.99Z"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 895 B |
@ -23,7 +23,7 @@ export const Document = ({
|
||||
...viewMeta
|
||||
}: DocumentProps) => {
|
||||
return (
|
||||
<div className={'flex w-full flex-col items-center'}>
|
||||
<div className={'flex h-full w-full flex-col items-center justify-center'}>
|
||||
<ViewMetaPreview {...viewMeta} />
|
||||
<Suspense fallback={<ComponentLoading />}>
|
||||
<div className={'mx-16 w-[964px] min-w-0 max-w-full'}>
|
||||
|
@ -2,7 +2,7 @@ import { BlockData, BlockType, InlineBlockType, YjsEditorKey } from '@/applicati
|
||||
import { BulletedList } from '@/components/editor/components/blocks/bulleted-list';
|
||||
import { Callout } from '@/components/editor/components/blocks/callout';
|
||||
import { CodeBlock } from '@/components/editor/components/blocks/code';
|
||||
import { DatabaseBlock } from '@/components/editor/components/blocks/database';
|
||||
// import { DatabaseBlock } from '@/components/editor/components/blocks/database';
|
||||
import { DividerNode } from '@/components/editor/components/blocks/divider';
|
||||
import { Heading } from '@/components/editor/components/blocks/heading';
|
||||
import { ImageBlock } from '@/components/editor/components/blocks/image';
|
||||
@ -70,10 +70,10 @@ export const Element = memo(
|
||||
return TableBlock;
|
||||
case BlockType.TableCell:
|
||||
return TableCellBlock;
|
||||
case BlockType.GridBlock:
|
||||
case BlockType.BoardBlock:
|
||||
case BlockType.CalendarBlock:
|
||||
return DatabaseBlock;
|
||||
// case BlockType.GridBlock:
|
||||
// case BlockType.BoardBlock:
|
||||
// case BlockType.CalendarBlock:
|
||||
// return DatabaseBlock;
|
||||
default:
|
||||
return UnSupportedBlock;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import { PublishProvider } from '@/application/publish';
|
||||
import { AFScroller } from '@/components/_shared/scroller';
|
||||
import { AFConfigContext } from '@/components/app/AppConfig';
|
||||
import CollabView from '@/components/publish/CollabView';
|
||||
import OutlineDrawer from '@/components/publish/outline/OutlineDrawer';
|
||||
import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys';
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { PublishViewHeader } from 'src/components/publish/header';
|
||||
import NotFound from '@/components/error/NotFound';
|
||||
@ -12,6 +14,8 @@ export interface PublishViewProps {
|
||||
publishName: string;
|
||||
}
|
||||
|
||||
const drawerWidth = 268;
|
||||
|
||||
export function PublishView({ namespace, publishName }: PublishViewProps) {
|
||||
const [doc, setDoc] = useState<YDoc | undefined>();
|
||||
const [notFound, setNotFound] = useState<boolean>(false);
|
||||
@ -35,6 +39,25 @@ export function PublishView({ namespace, publishName }: PublishViewProps) {
|
||||
void openPublishView();
|
||||
}, [openPublishView]);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
switch (true) {
|
||||
case createHotkey(HOT_KEY_NAME.TOGGLE_SIDEBAR)(e):
|
||||
e.preventDefault();
|
||||
setOpen((prev) => !prev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [onKeyDown]);
|
||||
if (notFound && !doc) {
|
||||
return <NotFound />;
|
||||
}
|
||||
@ -42,11 +65,22 @@ export function PublishView({ namespace, publishName }: PublishViewProps) {
|
||||
return (
|
||||
<PublishProvider namespace={namespace} publishName={publishName}>
|
||||
<div className={'h-screen w-screen'}>
|
||||
<PublishViewHeader />
|
||||
<PublishViewHeader
|
||||
onOpenDrawer={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
openDrawer={open}
|
||||
drawerWidth={drawerWidth}
|
||||
/>
|
||||
{open && <OutlineDrawer width={drawerWidth} open={open} onClose={() => setOpen(false)} />}
|
||||
|
||||
<AFScroller
|
||||
overflowXHidden
|
||||
style={{
|
||||
height: 'calc(100vh - 64px)',
|
||||
transform: open ? `translateX(${drawerWidth}px)` : 'none',
|
||||
width: open ? `calc(100% - ${drawerWidth}px)` : '100%',
|
||||
transition: 'width 0.2s ease-in-out 0s',
|
||||
}}
|
||||
className={'appflowy-layout appflowy-scroll-container'}
|
||||
>
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { ReactComponent as BoardSvg } from '$icons/16x/board.svg';
|
||||
import { ReactComponent as CalendarSvg } from '$icons/16x/date.svg';
|
||||
import { ReactComponent as DocumentSvg } from '$icons/16x/document.svg';
|
||||
import { ReactComponent as GridSvg } from '$icons/16x/grid.svg';
|
||||
import { ReactComponent as BoardSvg } from '@/assets/board.svg';
|
||||
import { ReactComponent as CalendarSvg } from '@/assets/calendar.svg';
|
||||
import { ReactComponent as DocumentSvg } from '@/assets/document.svg';
|
||||
import { ReactComponent as GridSvg } from '@/assets/grid.svg';
|
||||
import { ViewLayout } from '@/application/collab.type';
|
||||
import { usePublishContext } from '@/application/publish';
|
||||
import { notify } from '@/components/_shared/notify';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const renderCrumbIcon = (icon: string) => {
|
||||
export const renderCrumbIcon = (icon: string) => {
|
||||
if (Number(icon) === ViewLayout.Grid) {
|
||||
return <GridSvg className={'h-4 w-4'} />;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { IconButton } from '@mui/material';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import { ReactComponent as MoreIcon } from '@/assets/more.svg';
|
||||
import { ReactComponent as MoonIcon } from '@/assets/moon.svg';
|
||||
import { ReactComponent as SunIcon } from '@/assets/sun.svg';
|
||||
import { ReactComponent as ReportIcon } from '@/assets/report.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -27,7 +28,7 @@ function MoreActions() {
|
||||
return [
|
||||
isDark
|
||||
? {
|
||||
Icon: MoonIcon,
|
||||
Icon: SunIcon,
|
||||
label: t('settings.appearance.themeMode.light'),
|
||||
onClick: () => {
|
||||
setDark?.(false);
|
||||
|
@ -1,12 +1,23 @@
|
||||
import { usePublishContext } from '@/application/publish';
|
||||
import { openOrDownload } from '@/components/publish/header/utils';
|
||||
import { Divider } from '@mui/material';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Divider, IconButton } from '@mui/material';
|
||||
import { debounce } from 'lodash-es';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import OutlinePopover from '@/components/publish/outline/OutlinePopover';
|
||||
import Breadcrumb from './Breadcrumb';
|
||||
import { ReactComponent as Logo } from '@/assets/logo.svg';
|
||||
import MoreActions from './MoreActions';
|
||||
import { ReactComponent as SideOutlined } from '@/assets/side_outlined.svg';
|
||||
|
||||
export function PublishViewHeader() {
|
||||
export function PublishViewHeader({
|
||||
onOpenDrawer,
|
||||
drawerWidth,
|
||||
openDrawer,
|
||||
}: {
|
||||
onOpenDrawer: () => void;
|
||||
openDrawer: boolean;
|
||||
drawerWidth: number;
|
||||
}) {
|
||||
const viewMeta = usePublishContext()?.viewMeta;
|
||||
const crumbs = useMemo(() => {
|
||||
const ancestors = viewMeta?.ancestor_views.slice(1) || [];
|
||||
@ -29,10 +40,53 @@ export function PublishViewHeader() {
|
||||
};
|
||||
});
|
||||
}, [viewMeta]);
|
||||
const [openPopover, setOpenPopover] = React.useState(false);
|
||||
|
||||
const debounceClosePopover = useMemo(() => {
|
||||
return debounce(() => {
|
||||
setOpenPopover(false);
|
||||
}, 200);
|
||||
}, []);
|
||||
|
||||
const handleOpenPopover = useCallback(() => {
|
||||
debounceClosePopover.cancel();
|
||||
if (openDrawer) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpenPopover(true);
|
||||
}, [openDrawer, debounceClosePopover]);
|
||||
|
||||
return (
|
||||
<div className={'appflowy-top-bar flex h-[64px] px-5'}>
|
||||
<div
|
||||
style={{
|
||||
transform: openDrawer ? `translateX(${drawerWidth}px)` : 'none',
|
||||
width: openDrawer ? `calc(100% - ${drawerWidth}px)` : '100%',
|
||||
transition: 'width 0.2s ease-in-out 0s',
|
||||
}}
|
||||
className={'appflowy-top-bar flex h-[64px] px-5'}
|
||||
>
|
||||
<div className={'flex w-full items-center justify-between gap-2 overflow-hidden'}>
|
||||
{!openDrawer && (
|
||||
<OutlinePopover
|
||||
onMouseEnter={handleOpenPopover}
|
||||
onMouseLeave={debounceClosePopover}
|
||||
open={openPopover}
|
||||
onClose={debounceClosePopover}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setOpenPopover(false);
|
||||
onOpenDrawer();
|
||||
}}
|
||||
onMouseEnter={handleOpenPopover}
|
||||
onMouseLeave={debounceClosePopover}
|
||||
>
|
||||
<SideOutlined className={'h-4 w-4'} />
|
||||
</IconButton>
|
||||
</OutlinePopover>
|
||||
)}
|
||||
|
||||
<div className={'flex-1 overflow-hidden'}>
|
||||
<Breadcrumb crumbs={crumbs} />
|
||||
</div>
|
||||
|
@ -0,0 +1,61 @@
|
||||
import { PublishViewInfo, ViewLayout } from '@/application/collab.type';
|
||||
import OutlineItem from '@/components/publish/outline/OutlineItem';
|
||||
import SearchInput from '@/components/publish/outline/SearchInput';
|
||||
import { filterViews } from '@/components/publish/outline/utils';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
|
||||
function Outline({ viewMeta }: { viewMeta?: PublishViewInfo }) {
|
||||
const hasChildren = Boolean(viewMeta?.child_views?.length);
|
||||
|
||||
const [children, setChildren] = React.useState<PublishViewInfo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (viewMeta) {
|
||||
setChildren(viewMeta.child_views || []);
|
||||
}
|
||||
}, [viewMeta]);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(val: string) => {
|
||||
if (!val) {
|
||||
return setChildren(viewMeta?.child_views || []);
|
||||
}
|
||||
|
||||
setChildren(filterViews(viewMeta?.child_views || [], val));
|
||||
},
|
||||
[viewMeta]
|
||||
);
|
||||
|
||||
if (!viewMeta) {
|
||||
return <CircularProgress />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'flex w-full flex-1 flex-col items-start justify-between gap-2'}>
|
||||
<div
|
||||
style={{
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '44px',
|
||||
}}
|
||||
className={'z-10 flex items-center justify-center gap-3 bg-bg-body'}
|
||||
>
|
||||
<SearchInput onSearch={handleSearch} />
|
||||
</div>
|
||||
|
||||
{hasChildren && (
|
||||
<div className={'flex w-full flex-1 flex-col'}>
|
||||
{children
|
||||
.filter((view) => view.layout === ViewLayout.Document)
|
||||
.map((view: PublishViewInfo) => (
|
||||
<OutlineItem key={view.view_id} view={view} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Outline;
|
@ -0,0 +1,65 @@
|
||||
import { usePublishContext } from '@/application/publish';
|
||||
import { ReactComponent as AppflowyLogo } from '@/assets/appflowy.svg';
|
||||
import { ReactComponent as Logo } from '@/assets/logo.svg';
|
||||
import { ReactComponent as SideOutlined } from '@/assets/side_outlined.svg';
|
||||
import Outline from '@/components/publish/outline/Outline';
|
||||
import { createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys';
|
||||
import { Drawer, IconButton, Tooltip } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function OutlineDrawer({ open, width, onClose }: { open: boolean; width: number; onClose: () => void }) {
|
||||
const { t } = useTranslation();
|
||||
const viewMeta = usePublishContext()?.viewMeta;
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
sx={{
|
||||
width,
|
||||
flexShrink: 0,
|
||||
boxShadow: 'var(--shadow)',
|
||||
'& .MuiDrawer-paper': {
|
||||
width,
|
||||
boxSizing: 'border-box',
|
||||
borderColor: 'var(--line-border)',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
variant='persistent'
|
||||
anchor='left'
|
||||
open={open}
|
||||
tabIndex={0}
|
||||
autoFocus
|
||||
>
|
||||
<div className={'flex h-full flex-col'}>
|
||||
<div className={'flex h-[64px] items-center justify-between p-4'}>
|
||||
<div
|
||||
className={'flex cursor-pointer items-center text-text-title'}
|
||||
onClick={() => {
|
||||
window.open('https://appflowy.io', '_blank');
|
||||
}}
|
||||
>
|
||||
<Logo className={'h-5 w-5'} />
|
||||
<AppflowyLogo className={'w-24'} />
|
||||
</div>
|
||||
<Tooltip
|
||||
title={
|
||||
<div className={'flex flex-col'}>
|
||||
<span>{t('sideBar.closeSidebar')}</span>
|
||||
<span className={'text-xs text-text-caption'}>{createHotKeyLabel(HOT_KEY_NAME.TOGGLE_SIDEBAR)}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<IconButton onClick={onClose}>
|
||||
<SideOutlined className={'h-4 w-4 rotate-180 transform'} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={'flex flex-1 flex-col overflow-y-auto px-4 pb-4'}>
|
||||
<Outline viewMeta={viewMeta} />
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
export default OutlineDrawer;
|
@ -0,0 +1,84 @@
|
||||
import { PublishViewInfo, ViewLayout } from '@/application/collab.type';
|
||||
import { PublishContext } from '@/application/publish';
|
||||
import { notify } from '@/components/_shared/notify';
|
||||
import { renderCrumbIcon } from '@/components/publish/header/BreadcrumbItem';
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import { ReactComponent as ChevronDownIcon } from '@/assets/chevron_down.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function OutlineItem({ view }: { view: PublishViewInfo }) {
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
const getIcon = useCallback(() => {
|
||||
if (isExpanded) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsExpanded(false);
|
||||
}}
|
||||
>
|
||||
<ChevronDownIcon className={'h-4 w-4'} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsExpanded(true);
|
||||
}}
|
||||
>
|
||||
<ChevronDownIcon className={'h-4 w-4 -rotate-90 transform'} />
|
||||
</button>
|
||||
);
|
||||
}, [isExpanded]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigateToView = useContext(PublishContext)?.toView;
|
||||
const renderItem = (item: PublishViewInfo) => {
|
||||
return (
|
||||
<div className={'flex h-fit w-full flex-col gap-2'}>
|
||||
<div
|
||||
className={
|
||||
'flex w-full items-center rounded-[8px] p-1.5 text-sm hover:bg-content-blue-50 focus:bg-content-blue-50 focus:outline-none'
|
||||
}
|
||||
>
|
||||
{item.child_views?.length ? getIcon() : null}
|
||||
<div
|
||||
onClick={async () => {
|
||||
try {
|
||||
await navigateToView?.(item.view_id);
|
||||
} catch (e) {
|
||||
notify.error(t('publish.hasNotBeenPublished'));
|
||||
}
|
||||
}}
|
||||
className={'flex flex-1 cursor-pointer items-center gap-1'}
|
||||
>
|
||||
<div className={'icon'}>{renderCrumbIcon(item.icon?.value || String(item.layout))}</div>
|
||||
<div>{item.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'flex h-fit w-full flex-col'}>
|
||||
{renderItem(view)}
|
||||
<div
|
||||
className={'ml-9 flex transform flex-col gap-2 transition-all'}
|
||||
style={{
|
||||
height: isExpanded && view.child_views?.length ? 'auto' : 0,
|
||||
opacity: isExpanded && view.child_views?.length ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
{view.child_views
|
||||
?.filter((view) => view.layout === ViewLayout.Document)
|
||||
?.map((item, index) => (
|
||||
<OutlineItem key={index} view={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OutlineItem;
|
@ -0,0 +1,71 @@
|
||||
import { usePublishContext } from '@/application/publish';
|
||||
import Outline from '@/components/publish/outline/Outline';
|
||||
import { Divider, PopperPlacementType } from '@mui/material';
|
||||
import React, { ReactElement, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import RichTooltip from 'src/components/_shared/popover/RichTooltip';
|
||||
import { ReactComponent as Logo } from '@/assets/logo.svg';
|
||||
import { ReactComponent as AppflowyLogo } from '@/assets/appflowy.svg';
|
||||
|
||||
export function OutlinePopover({
|
||||
children,
|
||||
open,
|
||||
onClose,
|
||||
placement,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
children: ReactElement;
|
||||
placement?: PopperPlacementType;
|
||||
onMouseEnter?: () => void;
|
||||
onMouseLeave?: () => void;
|
||||
}) {
|
||||
const viewMeta = usePublishContext()?.viewMeta;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const content = useMemo(() => {
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
className={'flex h-fit max-h-[500px] w-[268px] flex-col overflow-y-auto p-2'}
|
||||
>
|
||||
<Outline viewMeta={viewMeta} />
|
||||
<div
|
||||
style={{
|
||||
position: 'sticky',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
height: '44px',
|
||||
}}
|
||||
className={'flex flex-col items-center justify-center gap-3 bg-bg-body'}
|
||||
>
|
||||
{Boolean(viewMeta?.child_views?.length) && <Divider className={'w-full'} />}
|
||||
|
||||
<div className={'flex w-full items-center justify-center gap-4 text-sm'}>
|
||||
<div className={'text-text-caption'}>{t('publish.createdWith')}</div>
|
||||
<div
|
||||
className={'flex cursor-pointer items-center justify-center text-text-title'}
|
||||
onClick={() => {
|
||||
window.open('https://appflowy.io', '_blank');
|
||||
}}
|
||||
>
|
||||
<Logo className={'h-4 w-4'} />
|
||||
<AppflowyLogo className={'w-20'} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [onMouseEnter, onMouseLeave, t, viewMeta]);
|
||||
|
||||
return (
|
||||
<RichTooltip open={open} onClose={onClose} content={content} placement={placement}>
|
||||
{children}
|
||||
</RichTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default OutlinePopover;
|
@ -0,0 +1,40 @@
|
||||
import { InputAdornment, OutlinedInput } from '@mui/material';
|
||||
import { debounce } from 'lodash-es';
|
||||
import React from 'react';
|
||||
import { ReactComponent as SearchIcon } from '@/assets/search.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function SearchInput({ onSearch }: { onSearch: (value: string) => void }) {
|
||||
const [value, setValue] = React.useState('');
|
||||
|
||||
const debounceSearch = React.useMemo(() => {
|
||||
return debounce((value: string) => {
|
||||
onSearch(value);
|
||||
}, 200);
|
||||
}, [onSearch]);
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(event.target.value);
|
||||
debounceSearch(event.target.value);
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<OutlinedInput
|
||||
autoFocus
|
||||
spellCheck={false}
|
||||
startAdornment={
|
||||
<InputAdornment className={'text-text-caption'} position='start'>
|
||||
<SearchIcon className={'h-4 w-4'} />
|
||||
</InputAdornment>
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder={t('search.label')}
|
||||
className={'h-[30px] w-full rounded-lg bg-bg-body'}
|
||||
value={value}
|
||||
size={'small'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchInput;
|
@ -0,0 +1 @@
|
||||
export * from './OutlinePopover';
|
@ -0,0 +1,21 @@
|
||||
import { PublishViewInfo } from '@/application/collab.type';
|
||||
|
||||
export function filterViews(views: PublishViewInfo[], keyword: string): PublishViewInfo[] {
|
||||
const filterAndFlatten = (views: PublishViewInfo[]): PublishViewInfo[] => {
|
||||
let result: PublishViewInfo[] = [];
|
||||
|
||||
for (const view of views) {
|
||||
if (view.name.toLowerCase().includes(keyword.toLowerCase())) {
|
||||
result.push(view);
|
||||
} else if (view.child_views) {
|
||||
const filteredChildren = filterAndFlatten(view.child_views);
|
||||
|
||||
result = result.concat(filteredChildren);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return filterAndFlatten(views);
|
||||
}
|
134
frontend/appflowy_web_app/src/utils/hotkeys.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
|
||||
export const isMac = () => {
|
||||
return navigator.userAgent.includes('Mac OS X');
|
||||
};
|
||||
|
||||
const MODIFIERS = {
|
||||
control: 'Ctrl',
|
||||
meta: '⌘',
|
||||
};
|
||||
|
||||
export const getModifier = () => {
|
||||
return isMac() ? MODIFIERS.meta : MODIFIERS.control;
|
||||
};
|
||||
|
||||
export enum HOT_KEY_NAME {
|
||||
LEFT = 'left',
|
||||
RIGHT = 'right',
|
||||
SELECT_ALL = 'select-all',
|
||||
ESCAPE = 'escape',
|
||||
ALIGN_LEFT = 'align-left',
|
||||
ALIGN_CENTER = 'align-center',
|
||||
ALIGN_RIGHT = 'align-right',
|
||||
BOLD = 'bold',
|
||||
ITALIC = 'italic',
|
||||
UNDERLINE = 'underline',
|
||||
STRIKETHROUGH = 'strikethrough',
|
||||
CODE = 'code',
|
||||
TOGGLE_TODO = 'toggle-todo',
|
||||
TOGGLE_COLLAPSE = 'toggle-collapse',
|
||||
INDENT_BLOCK = 'indent-block',
|
||||
OUTDENT_BLOCK = 'outdent-block',
|
||||
INSERT_SOFT_BREAK = 'insert-soft-break',
|
||||
SPLIT_BLOCK = 'split-block',
|
||||
BACKSPACE = 'backspace',
|
||||
OPEN_LINK = 'open-link',
|
||||
OPEN_LINKS = 'open-links',
|
||||
EXTEND_LINE_BACKWARD = 'extend-line-backward',
|
||||
EXTEND_LINE_FORWARD = 'extend-line-forward',
|
||||
PASTE = 'paste',
|
||||
PASTE_PLAIN_TEXT = 'paste-plain-text',
|
||||
HIGH_LIGHT = 'high-light',
|
||||
EXTEND_DOCUMENT_BACKWARD = 'extend-document-backward',
|
||||
EXTEND_DOCUMENT_FORWARD = 'extend-document-forward',
|
||||
SCROLL_TO_TOP = 'scroll-to-top',
|
||||
SCROLL_TO_BOTTOM = 'scroll-to-bottom',
|
||||
FORMAT_LINK = 'format-link',
|
||||
FIND_REPLACE = 'find-replace',
|
||||
/**
|
||||
* Navigation
|
||||
*/
|
||||
TOGGLE_THEME = 'toggle-theme',
|
||||
TOGGLE_SIDEBAR = 'toggle-sidebar',
|
||||
}
|
||||
|
||||
const defaultHotKeys = {
|
||||
[HOT_KEY_NAME.ALIGN_LEFT]: ['control+shift+l'],
|
||||
[HOT_KEY_NAME.ALIGN_CENTER]: ['control+shift+e'],
|
||||
[HOT_KEY_NAME.ALIGN_RIGHT]: ['control+shift+r'],
|
||||
[HOT_KEY_NAME.BOLD]: ['mod+b'],
|
||||
[HOT_KEY_NAME.ITALIC]: ['mod+i'],
|
||||
[HOT_KEY_NAME.UNDERLINE]: ['mod+u'],
|
||||
[HOT_KEY_NAME.STRIKETHROUGH]: ['mod+shift+s', 'mod+shift+x'],
|
||||
[HOT_KEY_NAME.CODE]: ['mod+e'],
|
||||
[HOT_KEY_NAME.TOGGLE_TODO]: ['mod+enter'],
|
||||
[HOT_KEY_NAME.TOGGLE_COLLAPSE]: ['mod+enter'],
|
||||
[HOT_KEY_NAME.SELECT_ALL]: ['mod+a'],
|
||||
[HOT_KEY_NAME.ESCAPE]: ['esc'],
|
||||
[HOT_KEY_NAME.INDENT_BLOCK]: ['tab'],
|
||||
[HOT_KEY_NAME.OUTDENT_BLOCK]: ['shift+tab'],
|
||||
[HOT_KEY_NAME.SPLIT_BLOCK]: ['enter'],
|
||||
[HOT_KEY_NAME.INSERT_SOFT_BREAK]: ['shift+enter'],
|
||||
[HOT_KEY_NAME.BACKSPACE]: ['backspace', 'shift+backspace'],
|
||||
[HOT_KEY_NAME.OPEN_LINK]: ['opt+enter'],
|
||||
[HOT_KEY_NAME.OPEN_LINKS]: ['opt+shift+enter'],
|
||||
[HOT_KEY_NAME.EXTEND_LINE_BACKWARD]: ['opt+shift+left'],
|
||||
[HOT_KEY_NAME.EXTEND_LINE_FORWARD]: ['opt+shift+right'],
|
||||
[HOT_KEY_NAME.PASTE]: ['mod+v'],
|
||||
[HOT_KEY_NAME.PASTE_PLAIN_TEXT]: ['mod+shift+v'],
|
||||
[HOT_KEY_NAME.HIGH_LIGHT]: ['mod+shift+h'],
|
||||
[HOT_KEY_NAME.EXTEND_DOCUMENT_BACKWARD]: ['mod+shift+up'],
|
||||
[HOT_KEY_NAME.EXTEND_DOCUMENT_FORWARD]: ['mod+shift+down'],
|
||||
[HOT_KEY_NAME.SCROLL_TO_TOP]: ['home'],
|
||||
[HOT_KEY_NAME.SCROLL_TO_BOTTOM]: ['end'],
|
||||
[HOT_KEY_NAME.TOGGLE_THEME]: ['mod+shift+l'],
|
||||
[HOT_KEY_NAME.TOGGLE_SIDEBAR]: ['mod+.'],
|
||||
[HOT_KEY_NAME.FORMAT_LINK]: ['mod+k'],
|
||||
[HOT_KEY_NAME.LEFT]: ['left'],
|
||||
[HOT_KEY_NAME.RIGHT]: ['right'],
|
||||
[HOT_KEY_NAME.FIND_REPLACE]: ['mod+f'],
|
||||
};
|
||||
|
||||
const replaceModifier = (hotkey: string) => {
|
||||
return hotkey.replace('mod', getModifier()).replace('control', 'ctrl');
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a hotkey checker.
|
||||
* @example trigger strike through when user press "Cmd + Shift + S" or "Cmd + Shift + X"
|
||||
* @param hotkeyName
|
||||
* @param customHotKeys
|
||||
*/
|
||||
export const createHotkey = (hotkeyName: HOT_KEY_NAME, customHotKeys?: Record<HOT_KEY_NAME, string[]>) => {
|
||||
const keys = customHotKeys || defaultHotKeys;
|
||||
const hotkeys = keys[hotkeyName];
|
||||
|
||||
return (event: KeyboardEvent) => {
|
||||
return hotkeys.some((hotkey) => {
|
||||
return isHotkey(hotkey, event);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a hotkey label.
|
||||
* eg. "Ctrl + B / ⌘ + B"
|
||||
* @param hotkeyName
|
||||
* @param customHotKeys
|
||||
*/
|
||||
export const createHotKeyLabel = (hotkeyName: HOT_KEY_NAME, customHotKeys?: Record<HOT_KEY_NAME, string[]>) => {
|
||||
const keys = customHotKeys || defaultHotKeys;
|
||||
const hotkeys = keys[hotkeyName].map((key) => replaceModifier(key));
|
||||
|
||||
return hotkeys
|
||||
.map((hotkey) =>
|
||||
hotkey
|
||||
.split('+')
|
||||
.map((key) => {
|
||||
return key === ' ' ? 'Space' : key.charAt(0).toUpperCase() + key.slice(1);
|
||||
})
|
||||
.join(' + ')
|
||||
)
|
||||
.join(' / ');
|
||||
};
|
@ -2037,6 +2037,7 @@
|
||||
"publish": {
|
||||
"hasNotBeenPublished": "This page hasn't been published yet",
|
||||
"reportPage": "Report page",
|
||||
"databaseHasNotBeenPublished": "This database hasn't been published yet"
|
||||
"databaseHasNotBeenPublished": "This database hasn't been published yet",
|
||||
"createdWith": "Created with"
|
||||
}
|
||||
}
|
||||
|