@@ -0,0 +1,104 @@ | |||||
/node_modules | |||||
/.pnp | |||||
.pnp.js | |||||
/coverage | |||||
/.next/ | |||||
/out/ | |||||
/build | |||||
.DS_Store | |||||
*.pem | |||||
npm-debug.log* | |||||
yarn-debug.log* | |||||
yarn-error.log* | |||||
.env.local | |||||
.env.development.local | |||||
.env.test.local | |||||
.env.production.local | |||||
.env | |||||
.vercel | |||||
.idea/ | |||||
logs | |||||
*.log | |||||
lerna-debug.log* | |||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||||
pids | |||||
*.pid | |||||
*.seed | |||||
*.pid.lock | |||||
lib-cov | |||||
coverage | |||||
*.lcov | |||||
.nyc_output | |||||
.grunt | |||||
bower_components | |||||
.lock-wscript | |||||
build/Release | |||||
node_modules/ | |||||
jspm_packages/ | |||||
web_modules/ | |||||
*.tsbuildinfo | |||||
.npm | |||||
.eslintcache | |||||
.rpt2_cache/ | |||||
.rts2_cache_cjs/ | |||||
.rts2_cache_es/ | |||||
.rts2_cache_umd/ | |||||
.node_repl_history | |||||
*.tgz | |||||
.yarn-integrity | |||||
.env.test | |||||
.cache | |||||
.parcel-cache | |||||
.next | |||||
out | |||||
.nuxt | |||||
dist | |||||
.cache/ | |||||
.vuepress/dist | |||||
.serverless/ | |||||
.fusebox/ | |||||
.dynamodb/ | |||||
.tern-port | |||||
.vscode-test | |||||
.yarn/cache | |||||
.yarn/unplugged | |||||
.yarn/build-state.yml | |||||
.yarn/install-state.gz | |||||
.pnp.* | |||||
Thumbs.db | |||||
Thumbs.db:encryptable | |||||
ehthumbs.db | |||||
ehthumbs_vista.db | |||||
*.stackdump | |||||
[Dd]esktop.ini | |||||
$RECYCLE.BIN/ | |||||
*.cab | |||||
*.msi | |||||
*.msix | |||||
*.msm | |||||
*.msp | |||||
*.lnk | |||||
cmake-build-*/ | |||||
*.iws | |||||
out/ | |||||
.idea_modules/ | |||||
atlassian-ide-plugin.xml | |||||
com_crashlytics_export_strings.xml | |||||
crashlytics.properties | |||||
crashlytics-build.properties | |||||
fabric.properties | |||||
.AppleDouble | |||||
.LSOverride | |||||
._* | |||||
.DocumentRevisions-V100 | |||||
.fseventsd | |||||
.Spotlight-V100 | |||||
.TemporaryItems | |||||
.Trashes | |||||
.VolumeIcon.icns | |||||
.com.apple.timemachine.donotpresent | |||||
.AppleDB | |||||
.AppleDesktop | |||||
Network Trash Folder | |||||
Temporary Items | |||||
.apdisk |
@@ -0,0 +1,34 @@ | |||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | |||||
## Getting Started | |||||
First, run the development server: | |||||
```bash | |||||
npm run dev | |||||
# or | |||||
yarn dev | |||||
``` | |||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | |||||
You can start editing the page by modifying `pages/index.ts`. The page auto-updates as you edit the file. | |||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. | |||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. | |||||
## Learn More | |||||
To learn more about Next.js, take a look at the following resources: | |||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | |||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | |||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! | |||||
## Deploy on Vercel | |||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | |||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. |
@@ -0,0 +1,4 @@ | |||||
module.exports = { | |||||
preset: 'ts-jest', | |||||
testEnvironment: 'node', | |||||
}; |
@@ -0,0 +1,2 @@ | |||||
/// <reference types="next" /> | |||||
/// <reference types="next/types/global" /> |
@@ -0,0 +1,3 @@ | |||||
module.exports = { | |||||
basePath: '', | |||||
} |
@@ -0,0 +1,31 @@ | |||||
{ | |||||
"name": "@zeichen/app-web", | |||||
"version": "0.1.0", | |||||
"private": true, | |||||
"scripts": { | |||||
"dev": "next dev", | |||||
"build": "next build", | |||||
"start": "next start", | |||||
"test": "jest" | |||||
}, | |||||
"homepage": "https://note.modal.sh", | |||||
"dependencies": { | |||||
"@tesseract-design/react-common": "^0.3.0", | |||||
"@tesseract-design/viewfinder": "0.1.1", | |||||
"mobiledoc-kit": "^0.13.2", | |||||
"next": "10.2.0", | |||||
"react": "17.0.2", | |||||
"react-dom": "17.0.2", | |||||
"react-mobiledoc-editor": "^0.11.1", | |||||
"styled-components": "5.2.3" | |||||
}, | |||||
"devDependencies": { | |||||
"@types/jest": "^26.0.23", | |||||
"@types/node": "^15.0.1", | |||||
"@types/react": "^17.0.4", | |||||
"@types/styled-components": "^5.1.9", | |||||
"jest": "^26.6.3", | |||||
"ts-jest": "^26.5.5", | |||||
"typescript": "^4.2.4" | |||||
} | |||||
} |
@@ -0,0 +1,77 @@ | |||||
body { | |||||
margin: 0; | |||||
/* overflow: overlay; */ | |||||
} | |||||
h1 { | |||||
font-size: 3em; | |||||
text-transform: lowercase; | |||||
} | |||||
h2 { | |||||
font-size: 2em; | |||||
text-transform: lowercase; | |||||
} | |||||
h3 { | |||||
font-size: 1.75em; | |||||
text-transform: lowercase; | |||||
} | |||||
h4 { | |||||
font-size: 1.5em; | |||||
text-transform: lowercase; | |||||
} | |||||
h5 { | |||||
font-size: 1.25em; | |||||
text-transform: lowercase; | |||||
} | |||||
h6 { | |||||
font-size: 1.125em; | |||||
text-transform: lowercase; | |||||
} | |||||
p { | |||||
margin: 2em 0; | |||||
} | |||||
li { | |||||
margin: 2em 0; | |||||
} | |||||
small { | |||||
font-size: 0.75em; | |||||
} | |||||
a:focus { | |||||
outline: 0; | |||||
} | |||||
pre { | |||||
overflow: auto; | |||||
margin: 0 -1rem; | |||||
padding: 0 1rem; | |||||
line-height: 1.2; | |||||
box-sizing: border-box; | |||||
} | |||||
@media only print { | |||||
pre, pre * { | |||||
color: inherit !important; | |||||
} | |||||
code, code * { | |||||
color: inherit !important; | |||||
} | |||||
img { | |||||
filter: grayscale(100%); | |||||
} | |||||
:root { | |||||
--color-accent: black !important; | |||||
--color-active: black !important; | |||||
} | |||||
} |
@@ -0,0 +1,166 @@ | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 400; | |||||
font-style: normal; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Regular.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 700; | |||||
font-style: normal; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Bold'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Bold.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 400; | |||||
font-style: italic; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Italic'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Italic.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 700; | |||||
font-style: italic; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Bold Italic'), | |||||
local('mononoki BoldItalic'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-BoldItalic.woff2) format('woff2'); | |||||
} | |||||
:root { | |||||
--font-family-base: 'Encode Sans', system-ui; | |||||
--font-stretch-base: semi-expanded; | |||||
--font-weight-base: 400; | |||||
--line-height-base: 1.75em; | |||||
--font-family-headings: 'Encode Sans', system-ui; | |||||
--font-stretch-headings: condensed; | |||||
--font-weight-headings: 100; | |||||
--line-height-headings: 1.125em; | |||||
--font-family-monospace: 'mononoki'; | |||||
--font-size-root: 16px; | |||||
--opacity-light: 0.25; | |||||
--opacity-lighter: 0.5; | |||||
--opacity-lightest: 0.75; | |||||
} | |||||
:root { | |||||
--color-bg: var(--color-negative, white); | |||||
--color-fg: var(--color-positive, black); | |||||
--color-accent: var(--color-primary, blue); | |||||
--color-active: var(--color-secondary, red); | |||||
} | |||||
@media (prefers-color-scheme: dark) { | |||||
:root { | |||||
--color-bg: var(--color-negative, black); | |||||
--color-fg: var(--color-positive, white); | |||||
} | |||||
} | |||||
:root { | |||||
font-size: var(--font-size-root); | |||||
font-family: var(--font-family-base), sans-serif; | |||||
font-stretch: var(--font-stretch-base, normal); | |||||
font-weight: var(--font-weight-base, 400); | |||||
line-height: var(--line-height-base, 1.75em); | |||||
transition-property: color, background-color; | |||||
transition-timing-function: ease; | |||||
transition-duration: 350ms; | |||||
} | |||||
@media only screen { | |||||
:root { | |||||
background-color: var(--color-bg); | |||||
color: var(--color-fg); | |||||
} | |||||
} | |||||
h1 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h2 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: calc(var(--font-weight-headings, 400) / 100 * 150); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h3 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: calc(var(--font-weight-headings, 400) / 100 * 250); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h4 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h5 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h6 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
code { | |||||
font-family: var(--font-family-monospace), monospace; | |||||
} | |||||
pre { | |||||
font-family: var(--font-family-monospace), monospace; | |||||
} | |||||
a { | |||||
color: inherit; | |||||
text-decoration: none; | |||||
} | |||||
@media only screen { | |||||
a { | |||||
color: var(--color-accent); | |||||
text-decoration: underline; | |||||
} | |||||
a:focus { | |||||
color: var(--color-active); | |||||
} | |||||
} | |||||
::selection { | |||||
background-color: var(--color-active); | |||||
color: var(--color-fg); | |||||
} | |||||
:root { | |||||
caret-color: var(--color-active); | |||||
} |
@@ -0,0 +1,19 @@ | |||||
:root { | |||||
--color-shade: #000; | |||||
--color-negative: #222; | |||||
--color-positive: #eee; | |||||
--color-primary: #C78AB3; | |||||
--color-secondary: #f90; | |||||
--color-code-number: #74f95e; | |||||
--color-code-keyword: #ff4389; | |||||
--color-code-type: #5097D2; | |||||
--color-code-instance-attribute: #76a7d2; | |||||
--color-code-function: #67c252; | |||||
--color-code-parameter: #915ec2; | |||||
--color-code-property: #ffa1c9; | |||||
--color-code-string: #eed371; | |||||
--color-code-variable: #8bc275; | |||||
--color-code-regexp: #74A72B; | |||||
--color-code-url: #0099CC; | |||||
--color-code-global: #C28050; | |||||
} |
@@ -0,0 +1,19 @@ | |||||
:root { | |||||
--color-shade: #fff; | |||||
--color-negative: #f8f8f8; | |||||
--color-positive: #333; | |||||
--color-primary: #ba6a9c; | |||||
--color-secondary: #f90; | |||||
--color-code-number: #72b507; | |||||
--color-code-keyword: #ee5189; | |||||
--color-code-type: #427fb1; | |||||
--color-code-instance-attribute: #76a7d2; | |||||
--color-code-function: #5a984a; | |||||
--color-code-parameter: #915ec2; | |||||
--color-code-property: #b76e8d; | |||||
--color-code-string: #b59e36; | |||||
--color-code-variable: #61864e; | |||||
--color-code-regexp: #4f7e03; | |||||
--color-code-url: #0099CC; | |||||
--color-code-global: #C28050; | |||||
} |
@@ -0,0 +1,4 @@ | |||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none" | |||||
xmlns="http://www.w3.org/2000/svg"> | |||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/> | |||||
</svg> |
@@ -0,0 +1,41 @@ | |||||
import Link from '../Link' | |||||
import styled from 'styled-components' | |||||
const BrandBase = styled(Link)({ | |||||
display: 'block', | |||||
textDecoration: 'none', | |||||
fontSize: '1.5rem', | |||||
fontWeight: 'bold', | |||||
fontStretch: '75%', | |||||
textTransform: 'uppercase', | |||||
width: '2rem', | |||||
textAlign: 'center', | |||||
'@media (min-width: 720px)': { | |||||
width: '8rem', | |||||
textAlign: 'left', | |||||
}, | |||||
}) | |||||
const Hide = styled('span')({ | |||||
display: 'none', | |||||
'@media (min-width: 720px)': { | |||||
display: 'inline', | |||||
}, | |||||
}) | |||||
const Brand = () => { | |||||
return ( | |||||
<BrandBase | |||||
href={{ | |||||
pathname: '/', | |||||
}} | |||||
> | |||||
Z | |||||
<Hide> | |||||
eichen | |||||
</Hide> | |||||
</BrandBase> | |||||
) | |||||
} | |||||
export default Brand |
@@ -0,0 +1,90 @@ | |||||
import * as React from 'react' | |||||
import * as T from '@tesseract-design/react-common' | |||||
import {Container, Editor as MobiledocEditor, MarkupButton, LinkButton} from 'react-mobiledoc-editor' | |||||
import styled, {CSSObject} from 'styled-components' | |||||
const TOOLBAR_BUTTON_COMMON_STYLES: CSSObject = { | |||||
border: 0, | |||||
backgroundColor: 'transparent', | |||||
padding: 0, | |||||
margin: 0, | |||||
height: '3rem', | |||||
width: '3rem', | |||||
color: 'inherit', | |||||
font: 'inherit', | |||||
} | |||||
const StyledMarkupButton = styled(MarkupButton)(TOOLBAR_BUTTON_COMMON_STYLES) | |||||
const StyledLinkButton = styled(LinkButton)(TOOLBAR_BUTTON_COMMON_STYLES) | |||||
const RawInput = styled('textarea')({ | |||||
color: 'inherit', | |||||
font: 'inherit', | |||||
padding: 0, | |||||
border: 0, | |||||
backgroundColor: 'transparent', | |||||
outline: 0, | |||||
display: 'block', | |||||
width: '100%', | |||||
resize: 'vertical', | |||||
}) | |||||
const ToolbarBase = styled('div')({ | |||||
display: 'flex', | |||||
alignItems: 'center', | |||||
flexWrap: 'wrap', | |||||
margin: '1rem 0', | |||||
}) | |||||
const StyledEditor = styled('div')({ | |||||
'> *': { | |||||
outline: 0, | |||||
}, | |||||
}) | |||||
const Editor = () => { | |||||
const [hydrated, setHydrated] = React.useState(false) | |||||
React.useEffect(() => { | |||||
setHydrated(true) | |||||
}, []) | |||||
if (hydrated) { | |||||
return ( | |||||
<Container> | |||||
<ToolbarBase> | |||||
<StyledMarkupButton | |||||
tag="strong" | |||||
> | |||||
<T.Icon | |||||
name="bold" | |||||
/> | |||||
</StyledMarkupButton> | |||||
<StyledMarkupButton | |||||
tag="em" | |||||
> | |||||
<T.Icon | |||||
name="italic" | |||||
/> | |||||
</StyledMarkupButton> | |||||
<StyledLinkButton> | |||||
<T.Icon | |||||
name="link" | |||||
/> | |||||
</StyledLinkButton> | |||||
</ToolbarBase> | |||||
<StyledEditor> | |||||
<MobiledocEditor /> | |||||
</StyledEditor> | |||||
</Container> | |||||
) | |||||
} | |||||
return ( | |||||
<RawInput | |||||
rows={20} | |||||
/> | |||||
) | |||||
} | |||||
export default Editor |
@@ -0,0 +1,68 @@ | |||||
import * as React from 'react' | |||||
import * as T from '@tesseract-design/react-common' | |||||
import styled from 'styled-components' | |||||
const Base = styled('div')({ | |||||
display: 'flex', | |||||
alignItems: 'center', | |||||
height: '4rem', | |||||
}) | |||||
const Content = styled('div')({ | |||||
lineHeight: 1.5, | |||||
flex: 'auto', | |||||
}) | |||||
const Title = styled('strong')({ | |||||
display: 'block', | |||||
whiteSpace: 'nowrap', | |||||
}) | |||||
const Subtitle = styled('small')({ | |||||
display: 'block', | |||||
whiteSpace: 'nowrap', | |||||
}) | |||||
const IconContainer = styled('div')({ | |||||
marginRight: '1rem', | |||||
}) | |||||
type Props = { | |||||
title: string, | |||||
subtitle?: string, | |||||
} | |||||
const FolderLinkContent: React.FC<Props> = ({ | |||||
title, | |||||
subtitle, | |||||
}) => { | |||||
return ( | |||||
<Base> | |||||
<IconContainer> | |||||
<T.Icon | |||||
name="folder" | |||||
/> | |||||
</IconContainer> | |||||
<Content> | |||||
<Title> | |||||
{title} | |||||
</Title> | |||||
{ | |||||
typeof (subtitle as string) | |||||
&& ( | |||||
<Subtitle> | |||||
{subtitle} | |||||
</Subtitle> | |||||
) | |||||
} | |||||
</Content> | |||||
<div> | |||||
<T.Icon | |||||
name="chevron-right" | |||||
/> | |||||
</div> | |||||
</Base> | |||||
) | |||||
} | |||||
export default FolderLinkContent |
@@ -0,0 +1,39 @@ | |||||
import * as React from 'react' | |||||
import NextLink from 'next/link' | |||||
import {UrlObject} from 'url' | |||||
type Props = { | |||||
href: UrlObject, | |||||
as?: UrlObject, | |||||
prefetch?: boolean, | |||||
replace?: boolean, | |||||
shallow?: boolean, | |||||
component?: React.ElementType, | |||||
} | |||||
const Link: React.FC<Props> = ({ | |||||
href, | |||||
as, | |||||
prefetch, | |||||
replace, | |||||
shallow, | |||||
component: Component = 'a', | |||||
...etcProps | |||||
}) => { | |||||
return ( | |||||
<NextLink | |||||
href={href} | |||||
as={as} | |||||
passHref | |||||
replace={replace} | |||||
shallow={shallow} | |||||
prefetch={prefetch} | |||||
> | |||||
<Component | |||||
{...etcProps} | |||||
/> | |||||
</NextLink> | |||||
) | |||||
} | |||||
export default Link |
@@ -0,0 +1,63 @@ | |||||
import * as React from 'react' | |||||
import * as T from '@tesseract-design/react-common' | |||||
import styled from 'styled-components' | |||||
const Base = styled('div')({ | |||||
display: 'flex', | |||||
alignItems: 'center', | |||||
height: '4rem', | |||||
}) | |||||
const Content = styled('div')({ | |||||
lineHeight: 1.5, | |||||
flex: 'auto', | |||||
}) | |||||
const Title = styled('strong')({ | |||||
display: 'block', | |||||
whiteSpace: 'nowrap', | |||||
}) | |||||
const Subtitle = styled('small')({ | |||||
display: 'block', | |||||
whiteSpace: 'nowrap', | |||||
}) | |||||
const IconContainer = styled('div')({ | |||||
marginRight: '1rem', | |||||
}) | |||||
type Props = { | |||||
title: string, | |||||
subtitle?: string, | |||||
} | |||||
const NoteLinkContent: React.FC<Props> = ({ | |||||
title, | |||||
subtitle, | |||||
}) => { | |||||
return ( | |||||
<Base> | |||||
<IconContainer> | |||||
<T.Icon | |||||
name="file-text" | |||||
/> | |||||
</IconContainer> | |||||
<Content> | |||||
<Title> | |||||
{title} | |||||
</Title> | |||||
{ | |||||
typeof (subtitle as string) | |||||
&& ( | |||||
<Subtitle> | |||||
{subtitle} | |||||
</Subtitle> | |||||
) | |||||
} | |||||
</Content> | |||||
</Base> | |||||
) | |||||
} | |||||
export default NoteLinkContent |
@@ -0,0 +1,83 @@ | |||||
import * as React from 'react' | |||||
import {LeftSidebarWithMenu} from '@tesseract-design/viewfinder' | |||||
import Link from '../../molecules/Link' | |||||
import {Subpage} from '../../../utils/query' | |||||
type Props = { | |||||
subpage: Subpage, | |||||
} | |||||
const BinView: React.FC<Props> = ({ | |||||
subpage | |||||
}) => { | |||||
return ( | |||||
<LeftSidebarWithMenu.Layout | |||||
brand={"Brand"} | |||||
sidebarMenuItems={[ | |||||
{ | |||||
id: 'notes', | |||||
url: { | |||||
pathname: '/my/notes', | |||||
}, | |||||
label: 'Notes', | |||||
icon: 'N', | |||||
}, | |||||
{ | |||||
id: 'bin', | |||||
url: { | |||||
pathname: '/my/bin' | |||||
}, | |||||
icon: 'B', | |||||
label: 'Bin', | |||||
secondary: true, | |||||
}, | |||||
]} | |||||
sidebarMainOpen={subpage === Subpage.SIDEBAR} | |||||
moreItemsOpen={subpage === Subpage.MORE} | |||||
sidebarMain={ | |||||
<LeftSidebarWithMenu.SidebarMainContainer> | |||||
Sidebar | |||||
</LeftSidebarWithMenu.SidebarMainContainer> | |||||
} | |||||
moreLinkMenuItem={{ | |||||
label: 'More', | |||||
icon: 'M', | |||||
url: { | |||||
query: { | |||||
subpage: 'more', | |||||
} | |||||
} | |||||
}} | |||||
moreLinkComponent={({ url, icon, label, }) => ( | |||||
<Link | |||||
href={url} | |||||
> | |||||
<LeftSidebarWithMenu.MoreSidebarMenuContainer> | |||||
<LeftSidebarWithMenu.MoreSidebarMenuItemIcon> | |||||
{icon} | |||||
</LeftSidebarWithMenu.MoreSidebarMenuItemIcon> | |||||
{label} | |||||
</LeftSidebarWithMenu.MoreSidebarMenuContainer> | |||||
</Link> | |||||
)} | |||||
linkComponent={({ url, icon, label, }) => ( | |||||
<Link | |||||
href={url} | |||||
> | |||||
<LeftSidebarWithMenu.SidebarMenuContainer> | |||||
<LeftSidebarWithMenu.SidebarMenuItemIcon> | |||||
{icon} | |||||
</LeftSidebarWithMenu.SidebarMenuItemIcon> | |||||
{label} | |||||
</LeftSidebarWithMenu.SidebarMenuContainer> | |||||
</Link> | |||||
)} | |||||
> | |||||
<LeftSidebarWithMenu.ContentContainer> | |||||
Hello | |||||
</LeftSidebarWithMenu.ContentContainer> | |||||
</LeftSidebarWithMenu.Layout> | |||||
) | |||||
} | |||||
export default BinView |
@@ -0,0 +1,232 @@ | |||||
import * as React from 'react' | |||||
import Head from 'next/head' | |||||
import * as T from '@tesseract-design/react-common' | |||||
import {LeftSidebarWithMenu} from '@tesseract-design/viewfinder' | |||||
import styled from 'styled-components' | |||||
import Link from '../../molecules/Link' | |||||
import {Subpage} from '../../../utils/query' | |||||
import Note from '../../../models/Note' | |||||
import Brand from '../../molecules/Brand' | |||||
import NoteLinkContent from '../../molecules/NoteLinkContent' | |||||
import Folder from '../../../models/Folder' | |||||
import FolderLinkContent from '../../molecules/FolderLinkContent' | |||||
import Editor from '../../molecules/Editor' | |||||
const SidebarLink = styled(Link)({ | |||||
textDecoration: 'none', | |||||
}) | |||||
const CenteredContent = styled(LeftSidebarWithMenu.ContentContainer)({ | |||||
height: 'calc(100vh - var(--height-topbar, 4rem) - var(--size-menu, 4rem))', | |||||
display: 'grid', | |||||
placeContent: 'center', | |||||
textAlign: 'center', | |||||
'@media (min-width: 720px)': { | |||||
height: 'calc(100vh - var(--height-topbar, 4rem))', | |||||
}, | |||||
}) | |||||
// @ts-ignore | |||||
const TitleInput = styled('input')({ | |||||
color: 'inherit', | |||||
font: 'inherit', | |||||
fontSize: '3rem', | |||||
padding: 0, | |||||
border: 0, | |||||
backgroundColor: 'transparent', | |||||
fontFamily: 'var(--font-family-headings), sans-serif', | |||||
fontStretch: 'var(--font-stretch-headings, normal)', | |||||
fontWeight: 'var(--font-weight-headings, 400)', | |||||
height: '4rem', | |||||
outline: 0, | |||||
width: '100%', | |||||
display: 'block', | |||||
marginBottom: '1rem', | |||||
}) | |||||
const SidebarTitle = styled('h1')({ | |||||
margin: 0, | |||||
lineHeight: '4rem', | |||||
}) | |||||
const TitleContainer = styled(LeftSidebarWithMenu.ContentContainer)({ | |||||
display: 'block', | |||||
}) | |||||
const SidebarSubtitle = styled('p')({ | |||||
margin: '2rem 0', | |||||
}) | |||||
type Props = { | |||||
subpage: Subpage, | |||||
notes: Note[], | |||||
subfolders: Folder[], | |||||
currentFolder: Folder, | |||||
currentNote?: Note, | |||||
} | |||||
const NoteView: React.FC<Props> = ({ | |||||
subpage, | |||||
notes, | |||||
subfolders, | |||||
currentFolder, | |||||
currentNote, | |||||
}) => { | |||||
const APP_NAME = 'Zeichen' | |||||
return ( | |||||
<> | |||||
<Head> | |||||
<title> | |||||
{currentNote ? `${currentNote.title} | ${APP_NAME}` : APP_NAME} | |||||
</title> | |||||
</Head> | |||||
<LeftSidebarWithMenu.Layout | |||||
brand={ | |||||
<Brand /> | |||||
} | |||||
sidebarMenuItems={[ | |||||
{ | |||||
id: 'notes', | |||||
url: { | |||||
pathname: '/my/notes', | |||||
}, | |||||
label: 'Notes', | |||||
icon: ( | |||||
<T.Icon | |||||
name="book" | |||||
/> | |||||
), | |||||
}, | |||||
{ | |||||
id: 'bin', | |||||
url: { | |||||
pathname: '/my/bin' | |||||
}, | |||||
icon: ( | |||||
<T.Icon | |||||
name="trash2" | |||||
/> | |||||
), | |||||
label: 'Bin', | |||||
secondary: true, | |||||
}, | |||||
]} | |||||
sidebarMainOpen={subpage === Subpage.SIDEBAR} | |||||
moreItemsOpen={subpage === Subpage.MORE} | |||||
sidebarMain={ | |||||
<> | |||||
{currentFolder && ( | |||||
<LeftSidebarWithMenu.SidebarMainContainer> | |||||
<SidebarTitle> | |||||
{currentFolder.name} | |||||
</SidebarTitle> | |||||
{currentFolder.description && ( | |||||
<SidebarSubtitle> | |||||
{currentFolder.description} | |||||
</SidebarSubtitle> | |||||
)} | |||||
</LeftSidebarWithMenu.SidebarMainContainer> | |||||
)} | |||||
{subfolders.map(f => ( | |||||
<SidebarLink | |||||
href={{ | |||||
pathname: '/my/notes', | |||||
query: { | |||||
folderId: f.id, | |||||
}, | |||||
}} | |||||
> | |||||
<LeftSidebarWithMenu.SidebarMainContainer> | |||||
<FolderLinkContent | |||||
title={f.name} | |||||
/> | |||||
</LeftSidebarWithMenu.SidebarMainContainer> | |||||
</SidebarLink> | |||||
))} | |||||
{notes.map(n => ( | |||||
<SidebarLink | |||||
href={{ | |||||
pathname: '/my/notes/[noteId]', | |||||
query: { | |||||
noteId: n.id, | |||||
}, | |||||
}} | |||||
> | |||||
<LeftSidebarWithMenu.SidebarMainContainer> | |||||
<NoteLinkContent | |||||
title={n.title} | |||||
subtitle={n.contentVersions[0].createdAt.toString()} | |||||
/> | |||||
</LeftSidebarWithMenu.SidebarMainContainer> | |||||
</SidebarLink> | |||||
))} | |||||
</> | |||||
} | |||||
moreLinkMenuItem={{ | |||||
label: 'More', | |||||
icon: ( | |||||
<T.Icon | |||||
name="more-horizontal" | |||||
/> | |||||
), | |||||
url: { | |||||
query: { | |||||
subpage: 'more', | |||||
} | |||||
} | |||||
}} | |||||
moreLinkComponent={({ url, icon, label, }) => ( | |||||
<Link | |||||
href={url} | |||||
> | |||||
<LeftSidebarWithMenu.MoreSidebarMenuContainer> | |||||
<LeftSidebarWithMenu.MoreSidebarMenuItemIcon> | |||||
{icon} | |||||
</LeftSidebarWithMenu.MoreSidebarMenuItemIcon> | |||||
{label} | |||||
</LeftSidebarWithMenu.MoreSidebarMenuContainer> | |||||
</Link> | |||||
)} | |||||
linkComponent={({ url, icon, label, }) => ( | |||||
<Link | |||||
href={url} | |||||
> | |||||
<LeftSidebarWithMenu.SidebarMenuContainer> | |||||
<LeftSidebarWithMenu.SidebarMenuItemIcon> | |||||
{icon} | |||||
</LeftSidebarWithMenu.SidebarMenuItemIcon> | |||||
{label} | |||||
</LeftSidebarWithMenu.SidebarMenuContainer> | |||||
</Link> | |||||
)} | |||||
> | |||||
{!currentNote && ( | |||||
<CenteredContent> | |||||
<T.Icon | |||||
name="folder" | |||||
size="4rem" | |||||
/> | |||||
Select a note from the menu. | |||||
</CenteredContent> | |||||
)} | |||||
{currentNote && ( | |||||
<> | |||||
<TitleContainer | |||||
as="label" | |||||
> | |||||
<TitleInput | |||||
placeholder="Title" | |||||
/> | |||||
</TitleContainer> | |||||
<LeftSidebarWithMenu.ContentContainer> | |||||
<Editor /> | |||||
</LeftSidebarWithMenu.ContentContainer> | |||||
</> | |||||
)} | |||||
</LeftSidebarWithMenu.Layout> | |||||
</> | |||||
) | |||||
} | |||||
export default NoteView |
@@ -0,0 +1,7 @@ | |||||
export default class Folder { | |||||
id: string | |||||
name: string | |||||
description?: string | |||||
} |
@@ -0,0 +1,15 @@ | |||||
import Folder from './Folder' | |||||
import User from './User' | |||||
import NoteVersion from './NoteVersion' | |||||
export default class Note { | |||||
id: string | |||||
title: string | |||||
folder?: Folder | |||||
authorUser: User | |||||
contentVersions: NoteVersion[] | |||||
} |
@@ -0,0 +1,11 @@ | |||||
import Note from './Note' | |||||
export default class NoteVersion { | |||||
id: string | |||||
content: string | |||||
createdAt: Date | string | |||||
parent?: NoteVersion | |||||
} |
@@ -0,0 +1,9 @@ | |||||
import UserProfile from './UserProfile' | |||||
export default class User { | |||||
id: string | |||||
username: string | |||||
profile: UserProfile | |||||
} |
@@ -0,0 +1,9 @@ | |||||
export default class UserLink { | |||||
id: string | |||||
title: string | |||||
description?: string | |||||
url: string | |||||
} |
@@ -0,0 +1,11 @@ | |||||
import UserLink from './UserLink' | |||||
export default class UserProfile { | |||||
id: string | |||||
email: string | |||||
bio?: string | |||||
links: UserLink[] | |||||
} |
@@ -0,0 +1,5 @@ | |||||
const MyApp = ({ Component, pageProps }) => { | |||||
return <Component {...pageProps} /> | |||||
} | |||||
export default MyApp |
@@ -0,0 +1,61 @@ | |||||
import Document, {Html, Head, Main, NextScript} from 'next/document' | |||||
import {ServerStyleSheet} from 'styled-components' | |||||
import pkg from '../../package.json' | |||||
import config from '../../next.config' | |||||
const publicUrl = process.env.NODE_ENV === 'production' ? pkg.homepage : config.basePath | |||||
export default class MyDocument extends Document { | |||||
static async getInitialProps(ctx) { | |||||
const sheet = new ServerStyleSheet() | |||||
const originalRenderPage = ctx.renderPage | |||||
try { | |||||
ctx.renderPage = () => | |||||
originalRenderPage({ | |||||
enhanceApp: (App) => (props) => | |||||
sheet.collectStyles( | |||||
<App | |||||
{...props} | |||||
/>, | |||||
), | |||||
}) | |||||
const initialProps = await Document.getInitialProps(ctx) | |||||
return { | |||||
...initialProps, | |||||
styles: ( | |||||
<> | |||||
{initialProps.styles} | |||||
<link rel="preconnect" href="https://fonts.gstatic.com" /> | |||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Encode+Sans:wdth,wght@75..112.5,100..900&display=swap" /> | |||||
<link rel="stylesheet" href={`${publicUrl}/global.css`} /> | |||||
<link rel="stylesheet" href={`${publicUrl}/theme.css`} /> | |||||
<link rel="stylesheet" title="Dark" href={`${publicUrl}/theme/dark.css`} /> | |||||
<link rel="alternate stylesheet" title="Light" href={`${publicUrl}/theme/light.css`} /> | |||||
{sheet.getStyleElement()} | |||||
</> | |||||
), | |||||
} | |||||
} catch (err) { | |||||
console.error(err) | |||||
} finally { | |||||
sheet.seal() | |||||
} | |||||
} | |||||
render() { | |||||
return ( | |||||
<Html | |||||
lang="en-PH" | |||||
> | |||||
<Head /> | |||||
<body> | |||||
<Main /> | |||||
<NextScript /> | |||||
</body> | |||||
</Html> | |||||
) | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
import {GetServerSideProps, NextPage} from 'next' | |||||
type Props = {} | |||||
const Page: NextPage<Props> = () => { | |||||
return ( | |||||
<> | |||||
Hello | |||||
</> | |||||
); | |||||
}; | |||||
export default Page | |||||
export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||||
return { | |||||
props: {}, | |||||
} | |||||
} |
@@ -0,0 +1,28 @@ | |||||
import {GetServerSideProps, NextPage} from 'next' | |||||
import BinView from '../../../components/templates/BinView' | |||||
import {QueryFragment, Subpage} from '../../../utils/query' | |||||
type Props = { | |||||
subpage: Subpage, | |||||
} | |||||
const Page: NextPage<Props> = ({ | |||||
subpage, | |||||
}) => { | |||||
return ( | |||||
<BinView | |||||
subpage={subpage} | |||||
/> | |||||
); | |||||
}; | |||||
export default Page | |||||
export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||||
const { [QueryFragment.SUBPAGE]: subpage = '' } = ctx.query | |||||
return { | |||||
props: { | |||||
subpage, | |||||
}, | |||||
} | |||||
} |
@@ -0,0 +1,85 @@ | |||||
import {GetServerSideProps, NextPage} from 'next' | |||||
import NoteView from '../../../components/templates/NoteView' | |||||
import {QueryFragment, Subpage} from '../../../utils/query' | |||||
import Note from '../../../models/Note' | |||||
import Folder from '../../../models/Folder' | |||||
type Props = { | |||||
subpage: Subpage, | |||||
notes: Note[], | |||||
currentFolder?: Folder, | |||||
subfolders: Folder[], | |||||
currentNote: Note, | |||||
} | |||||
const Page: NextPage<Props> = ({ | |||||
subpage, | |||||
notes, | |||||
currentFolder, | |||||
subfolders, | |||||
currentNote, | |||||
}) => { | |||||
return ( | |||||
<NoteView | |||||
subpage={subpage} | |||||
notes={notes} | |||||
subfolders={subfolders} | |||||
currentFolder={currentFolder} | |||||
currentNote={currentNote} | |||||
/> | |||||
); | |||||
}; | |||||
export default Page | |||||
export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||||
const { [QueryFragment.SUBPAGE]: subpage = '', noteId, } = ctx.query | |||||
const authorUser = { | |||||
id: '0', | |||||
profile: { | |||||
id: '0', | |||||
email: 'hello@example.com', | |||||
links: [], | |||||
}, | |||||
username: 'johndoe' | |||||
} | |||||
const notes: Note[] = [ | |||||
{ | |||||
id: '0', | |||||
title: 'Hello', | |||||
authorUser: authorUser, | |||||
contentVersions: [ | |||||
{ | |||||
id: '0', | |||||
content: 'Note content', | |||||
createdAt: new Date().toISOString(), | |||||
} | |||||
], | |||||
} | |||||
] | |||||
const subfolders: Folder[] = [ | |||||
{ | |||||
id: '0', | |||||
name: 'Child Folder', | |||||
description: 'Where we put other notes', | |||||
} | |||||
] | |||||
const currentNote: Note = { | |||||
id: noteId as string, | |||||
title: 'This Note', | |||||
authorUser, | |||||
contentVersions: [], | |||||
} | |||||
return { | |||||
props: { | |||||
subpage, | |||||
notes, | |||||
subfolders, | |||||
currentFolder: { | |||||
name: 'Root Folder', | |||||
description: 'Default location of your notes.', | |||||
}, | |||||
currentNote, | |||||
}, | |||||
} | |||||
} |
@@ -0,0 +1,75 @@ | |||||
import {GetServerSideProps, NextPage} from 'next' | |||||
import NoteView from '../../../components/templates/NoteView' | |||||
import {QueryFragment, Subpage} from '../../../utils/query' | |||||
import Note from '../../../models/Note' | |||||
import Folder from '../../../models/Folder' | |||||
type Props = { | |||||
subpage: Subpage, | |||||
notes: Note[], | |||||
currentFolder?: Folder, | |||||
subfolders: Folder[], | |||||
} | |||||
const Page: NextPage<Props> = ({ | |||||
subpage, | |||||
notes, | |||||
currentFolder, | |||||
subfolders, | |||||
}) => { | |||||
return ( | |||||
<NoteView | |||||
subpage={subpage} | |||||
notes={notes} | |||||
subfolders={subfolders} | |||||
currentFolder={currentFolder} | |||||
/> | |||||
); | |||||
}; | |||||
export default Page | |||||
export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||||
const { [QueryFragment.SUBPAGE]: subpage = '' } = ctx.query | |||||
const authorUser = { | |||||
id: '0', | |||||
profile: { | |||||
id: '0', | |||||
email: 'hello@example.com', | |||||
links: [], | |||||
}, | |||||
username: 'johndoe' | |||||
} | |||||
const notes: Note[] = [ | |||||
{ | |||||
id: '0', | |||||
title: 'Hello', | |||||
authorUser: authorUser, | |||||
contentVersions: [ | |||||
{ | |||||
id: '0', | |||||
content: 'Note content', | |||||
createdAt: new Date().toISOString(), | |||||
} | |||||
], | |||||
} | |||||
] | |||||
const subfolders: Folder[] = [ | |||||
{ | |||||
id: '0', | |||||
name: 'Child Folder', | |||||
description: 'Where we put other notes', | |||||
} | |||||
] | |||||
return { | |||||
props: { | |||||
subpage, | |||||
notes, | |||||
subfolders, | |||||
currentFolder: { | |||||
name: 'Root Folder', | |||||
description: 'Default location of your notes.', | |||||
}, | |||||
}, | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
import {deserialize} from './content' | |||||
describe('deserialize', () => { | |||||
it('should parse BBCode', () => { | |||||
expect(deserialize(` | |||||
hello world | |||||
`)).toEqual({ | |||||
version: '0.3.2', | |||||
markups: [], | |||||
atoms: [], | |||||
cards: [], | |||||
sections: [ | |||||
[1, 'p', [ | |||||
[0, [], 0, 'hello world'] | |||||
]] | |||||
], | |||||
}) | |||||
}) | |||||
}) |
@@ -0,0 +1,160 @@ | |||||
enum MarkerType { | |||||
TEXT = 0, | |||||
ATOM = 1, | |||||
} | |||||
enum SectionType { | |||||
MARKUP = 1, | |||||
IMAGE = 2, | |||||
LIST = 3, | |||||
CARD = 10, | |||||
} | |||||
// TODO!!!!! | |||||
// Try parsing BBCode before converting it to Mobiledoc and vv | |||||
const generateBBCodeAST = (bbcode: string) => ( | |||||
bbcode | |||||
.trim() | |||||
.split('') | |||||
.reduce( | |||||
(parserState, c) => { | |||||
const {tokenStack, tagStack, tree} = parserState | |||||
const [lastToken] = tokenStack.slice(-1) | |||||
switch (c) { | |||||
case '[': | |||||
return { | |||||
...parserState, | |||||
tokenStack: [ | |||||
...tokenStack, | |||||
'', | |||||
], | |||||
} | |||||
case ']': | |||||
if ( | |||||
lastToken.startsWith('/') | |||||
&& lastToken.slice('/'.length) === tagStack.slice(-1)[0] | |||||
) { | |||||
return { | |||||
...parserState, | |||||
tokenStack: tokenStack.slice(0, -1), | |||||
tagStack: tagStack.slice(0, -1), | |||||
} | |||||
} | |||||
return { | |||||
...parserState, | |||||
tokenStack: tokenStack.slice(0, -1), | |||||
tagStack: [ | |||||
...tagStack, | |||||
lastToken, | |||||
] | |||||
} | |||||
default: | |||||
break | |||||
} | |||||
return { | |||||
...parserState, | |||||
tokenStack: [ | |||||
...tokenStack.slice(0, -1), | |||||
lastToken + c, | |||||
], | |||||
} | |||||
}, | |||||
{ | |||||
tree: [], | |||||
tokenStack: [''], | |||||
tagStack: [], | |||||
} | |||||
) | |||||
) | |||||
const convertBBCodeToMobiledoc = (bbcode: string, { components, version = '0.3.2,' }) => { | |||||
return bbcode | |||||
.trim() | |||||
.split('') | |||||
.reduce( | |||||
(parserState, c) => { | |||||
switch (c) { | |||||
case '[': | |||||
return { | |||||
...parserState, | |||||
tokenStack: [ | |||||
...parserState.tokenStack, | |||||
'', | |||||
] | |||||
} | |||||
case ']': | |||||
if (parserState.lastToken.startsWith('/')) { | |||||
return { | |||||
...parserState, | |||||
// pop token stack | |||||
tokenStack: parserState.tokenStack.slice(0, -1), | |||||
tagStack: parserState.tagStack.slice(0, -1), | |||||
} | |||||
} | |||||
return { | |||||
...parserState, | |||||
// pop token stack | |||||
tokenStack: parserState.tokenStack.slice(0, -1), | |||||
tagStack: [ | |||||
...parserState.tagStack, | |||||
parserState.lastToken, | |||||
], | |||||
lastToken: '', | |||||
} | |||||
} | |||||
let [lastSection = [SectionType.MARKUP, 'p', []]] = parserState.state.sections.slice(-1) | |||||
let [sectionType = SectionType.MARKUP, sectionTag = 'p', sectionMarkers = []] = lastSection | |||||
let [lastMarker = [MarkerType.TEXT, [], 0, '']] = sectionMarkers.slice(-1) | |||||
let [textTypeIdentifier, openMarkupsIndexes, numberOfClosedMarkups, value] = lastMarker | |||||
return { | |||||
...parserState, | |||||
sections: [ | |||||
...parserState.state.sections, | |||||
[ | |||||
sectionType, | |||||
sectionTag, | |||||
[ | |||||
...sectionMarkers, | |||||
[ | |||||
textTypeIdentifier, | |||||
openMarkupsIndexes, | |||||
numberOfClosedMarkups, | |||||
value + parserState.lastCharacter, | |||||
], | |||||
], | |||||
], | |||||
], | |||||
lastCharacter: c, | |||||
} | |||||
}, | |||||
{ | |||||
state: { | |||||
version, | |||||
markups: [], | |||||
atoms: [], | |||||
cards: [], | |||||
sections: [], | |||||
}, | |||||
lastCharacter: '', | |||||
lastToken: '', | |||||
tokenStack: [], | |||||
tagStack: [], | |||||
} | |||||
) | |||||
.state | |||||
} | |||||
const convertMobiledocToBBCode = (mobiledoc: any, { components }) => { | |||||
} | |||||
export const deserialize = (text: string) => { | |||||
return convertBBCodeToMobiledoc(text, { components: [] }) | |||||
} | |||||
export const serialize = (data: any) => { | |||||
return convertMobiledocToBBCode(data, { components: [] }) | |||||
} |
@@ -0,0 +1,8 @@ | |||||
export enum Subpage { | |||||
SIDEBAR = 'sidebar', | |||||
MORE = 'more', | |||||
} | |||||
export enum QueryFragment { | |||||
SUBPAGE = 'subpage', | |||||
} |
@@ -0,0 +1,29 @@ | |||||
{ | |||||
"compilerOptions": { | |||||
"target": "es5", | |||||
"lib": [ | |||||
"dom", | |||||
"dom.iterable", | |||||
"esnext" | |||||
], | |||||
"allowJs": true, | |||||
"skipLibCheck": true, | |||||
"strict": false, | |||||
"forceConsistentCasingInFileNames": true, | |||||
"noEmit": true, | |||||
"esModuleInterop": true, | |||||
"module": "esnext", | |||||
"moduleResolution": "node", | |||||
"resolveJsonModule": true, | |||||
"isolatedModules": true, | |||||
"jsx": "preserve" | |||||
}, | |||||
"include": [ | |||||
"next-env.d.ts", | |||||
"**/*.ts", | |||||
"**/*.tsx" | |||||
], | |||||
"exclude": [ | |||||
"node_modules" | |||||
] | |||||
} |