chore: restructuring of tests

pull/81/head
Eric Maciel 2 years ago
parent ddd1892dd1
commit 9992397f92

@ -48,7 +48,7 @@ const decorator = useCursor(editor)
## Backend
```ts
const { AutomergeCollaboration } = require('@hiveteams/collab-backend')
const { AutomergeCollaboration } = require('backend/index')
const collabBackend = new AutomergeCollaboration(options)
```

@ -1,6 +0,0 @@
{
"lerna": "2.7.1",
"version": "0.7.30",
"npmClient": "yarn",
"useWorkspaces": true
}

@ -1,23 +1,11 @@
{
"name": "@hiveteams/collab",
"private": true,
"description": "Slate collaborative plugin & microservice",
"description": "Slate collaborative editing",
"scripts": {
"bootstrap": "lerna bootstrap",
"version": "auto-changelog -p ./packages/bridge/package.json --template changelog-template.hbs && git add CHANGELOG.md",
"clean": "rimraf ./packages/**/lib/ && rimraf ./packages/**/tsconfig.tsbuildinfo && lerna clean --yes",
"release": "yarn prebuild && yarn build && lerna version && lerna publish from-package",
"deploy:site": "git subtree push --prefix packages/example heroku master",
"dev": "lerna run --stream build:js && concurrently \"yarn watch\" \"lerna run dev --stream\"",
"build": "lerna run build:module --stream",
"watch": "lerna run --parallel watch",
"clean:module": "lerna clean --yes",
"prebuild": "yarn clean",
"test": "lerna run test --stream",
"format": "prettier --write"
"format": "prettier --write",
"test": "jest"
},
"workspaces": [
"packages/*"
],
"author": "cudr",
"license": "MIT",
"repository": {
@ -32,19 +20,51 @@
},
"lint-staged": {
"*.{js,jsx,ts,tsx,babelrc}": [
"yarn run format",
"npm run format",
"git add"
]
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-typescript": "^7.6.0",
"@commitlint/cli": "^9.0.1",
"@commitlint/config-conventional": "^9.0.1",
"@types/jest": "^24.9.0",
"@types/lodash": "^4.14.178",
"@types/socket.io": "^2.1.4",
"auto-changelog": "^2.1.0",
"concurrently": "^4.1.2",
"husky": "^3.0.5",
"lerna": "^3.20.2",
"jest": "^24.9.0",
"lint-staged": "^9.2.5",
"prettier": "^1.18.2",
"rimraf": "^3.0.2"
"ts-jest": "^25.4.0",
"typescript": "^4.5.5"
},
"dependencies": {
"automerge": "0.14.0",
"lodash": "4.17.21",
"slate": "0.72.8",
"slate-history": "0.66.0",
"socket.io": "^2.3.0"
},
"jest": {
"preset": "ts-jest",
"globals": {
"ts-jest": {
"babelConfig": ".babelrc"
}
},
"roots": [
"<rootDir>/src"
],
"transform": {
"^.+\\.ts?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$"
}
}

@ -1,9 +0,0 @@
{
"presets": ["@babel/preset-env", "@babel/typescript"],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread",
"@babel/plugin-proposal-optional-chaining"
]
}

@ -1,9 +0,0 @@
The MIT License
Copyright &copy; 20192020, [George Kukushin](https://github.com/cudr)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,51 +0,0 @@
{
"name": "@hiveteams/collab-backend",
"version": "0.7.31",
"files": [
"lib"
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"description": "slate-collaborative backend",
"repository": {
"type": "git",
"url": "git+https://github.com/cudr/slate-collaborative.git"
},
"publishConfig": {
"access": "public"
},
"author": "cudr",
"license": "MIT",
"scripts": {
"prepublishOnly": "yarn run build:js",
"build:js": "./node_modules/typescript/bin/tsc -p ./tsconfig.json",
"watch": "yarn build:js -w"
},
"dependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/runtime": "^7.6.3",
"@hiveteams/collab-bridge": "^0.7.27",
"@types/debug": "^4.1.5",
"@types/lodash": "^4.14.150",
"@types/socket.io": "^2.1.4",
"automerge": "0.14.0",
"debug": "^4.2.0",
"lodash": "^4.17.15",
"slate": "0.72.8",
"socket.io": "^2.3.0",
"typescript": "4.5.5"
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@babel/plugin-transform-runtime": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-typescript": "^7.6.0"
},
"directories": {
"lib": "lib"
},
"gitHead": "89dd1657ba1b39db298e00a380f45089b8b52a91"
}

@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"include": ["./src/**/*"],
"compilerOptions": {
"rootDir": "./src",
"baseUrl": "./src",
"outDir": "./lib",
"composite": true,
"declaration": true,
"declarationMap": true,
"paths": {
"@hiveteams/collab-bridge": ["../../collab-bridge"]
}
},
"references": [{ "path": "../bridge" }]
}

@ -1,9 +0,0 @@
The MIT License
Copyright &copy; 20192020, [George Kukushin](https://github.com/cudr)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,67 +0,0 @@
{
"name": "@hiveteams/collab-bridge",
"version": "0.7.31",
"files": [
"lib"
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"description": "slate-collaborative bridge",
"repository": {
"type": "git",
"url": "git+https://github.com/cudr/slate-collaborative.git"
},
"publishConfig": {
"access": "public"
},
"author": "cudr",
"license": "MIT",
"scripts": {
"prepublishOnly": "yarn run build:module",
"build:module": "yarn run build:js",
"build:js": "./node_modules/typescript/bin/tsc -p ./tsconfig.json",
"watch": "yarn build:js -w",
"test": "jest"
},
"dependencies": {
"automerge": "0.14.0",
"slate": "0.72.8",
"typescript": "4.5.5"
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-typescript": "^7.6.0",
"@types/jest": "^24.9.0",
"jest": "^24.9.0",
"ts-jest": "^25.4.0"
},
"directories": {
"lib": "lib"
},
"keywords": [
"slate",
"automerge",
"bridge"
],
"jest": {
"preset": "ts-jest",
"globals": {
"ts-jest": {
"babelConfig": ".babelrc"
}
},
"roots": [
"<rootDir>/src"
],
"transform": {
"^.+\\.ts?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$"
},
"gitHead": "89dd1657ba1b39db298e00a380f45089b8b52a91"
}

@ -1,13 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"include": ["./src/**/*"],
"compilerOptions": {
"target": "es2019",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./lib",
"composite": true,
"declaration": true,
"declarationMap": true
}
}

@ -1,8 +0,0 @@
{
"presets": ["@babel/env", "@babel/react", "@babel/typescript"],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread",
"@babel/plugin-proposal-optional-chaining"
]
}

@ -1,9 +0,0 @@
The MIT License
Copyright &copy; 20192020, [George Kukushin](https://github.com/cudr)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,71 +0,0 @@
{
"name": "@hiveteams/collab-client",
"version": "0.7.30",
"files": [
"lib"
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"description": "slate-collaborative client",
"repository": {
"type": "git",
"url": "git+https://github.com/cudr/slate-collaborative.git"
},
"publishConfig": {
"access": "public"
},
"author": "cudr",
"license": "MIT",
"scripts": {
"prepublishOnly": "npm run build:module",
"build:module": "npm run build:types && npm run build:js",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
"watch": "yarn build:js -w",
"test": "DEBUG=app* jest"
},
"dependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-react": "^7.0.0",
"@hiveteams/collab-bridge": "^0.7.27",
"automerge": "0.14.0",
"lodash": "^4.17.20",
"slate": "0.72.8",
"slate-history": "0.66.0",
"socket.io-client": "^2.3.0",
"typescript": "4.5.5"
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@babel/preset-env": "^7.6.0",
"@babel/preset-typescript": "^7.6.0",
"@hiveteams/collab-backend": "^0.7.30",
"@types/jest": "^24.9.0",
"@types/react": "^16.9.34",
"@types/socket.io-client": "^1.4.32",
"jest": "^26.6.3",
"ts-jest": "^26.4.4"
},
"directories": {
"lib": "lib"
},
"gitHead": "89dd1657ba1b39db298e00a380f45089b8b52a91",
"jest": {
"preset": "ts-jest",
"globals": {
"ts-jest": {
"babelConfig": ".babelrc"
}
},
"roots": [
"<rootDir>/src"
],
"transform": {
"^.+\\.ts?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$"
}
}

@ -1,15 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"include": ["./src/**/*"],
"compilerOptions": {
"rootDir": "./src",
"baseUrl": "./src",
"outDir": "./lib",
"composite": true,
"paths": {
"@hiveteams/collab-bridge": ["../../collab-bridge"],
"@hiveteams/collab-backend": ["../../collab-backend"]
}
},
"references": [{ "path": "../bridge" }, { "path": "../backend" }]
}

@ -1,58 +0,0 @@
{
"name": "@hiveteams/collab-example",
"version": "0.7.30",
"private": true,
"dependencies": {
"@emotion/core": "^10.0.17",
"@emotion/styled": "^10.0.17",
"@hiveteams/collab-backend": "^0.7.30",
"@hiveteams/collab-client": "^0.7.30",
"@types/faker": "^4.1.5",
"@types/is-url": "^1.2.28",
"@types/jest": "24.0.18",
"@types/node": "12.7.5",
"@types/randomcolor": "^0.5.4",
"@types/react-dom": "^16.9.6",
"concurrently": "^4.1.2",
"cross-env": "^6.0.3",
"express": "^4.17.1",
"faker": "^4.1.0",
"is-url": "^1.2.4",
"lodash": "^4.17.15",
"nodemon": "^1.19.2",
"randomcolor": "^0.5.4",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-scripts": "3.1.2",
"slate": "0.58.3",
"slate-history": "0.58.3",
"slate-react": "0.58.3",
"typescript": "^3.8.3"
},
"scripts": {
"start": "node server.js",
"start:cra": "react-scripts start",
"prebuild": "cp -f ./tsconfig.production.json ./tsconfig.json",
"build": "cross-env NODE_ENV=production && react-scripts build",
"dev": "concurrently \"yarn start:cra\" \"yarn serve\"",
"serve": "nodemon --watch ../backend/lib --inspect server.js"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"engines": {
"node": "12.x"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
/>
<meta name="theme-color" content="#db7093" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Slate-collaborative. Collaboration plugin and microservice</title>
</head>
<body>
<noscript></noscript>
<div id="root"></div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

@ -1,25 +0,0 @@
{
"short_name": "Slate collaborative",
"name": "collaborative plugin & microservice",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#db7093",
"background_color": "#ffffff"
}

@ -1,2 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

@ -1,39 +0,0 @@
const { AutomergeCollaboration } = require('@hiveteams/collab-backend')
const express = require('express')
const defaultValue = [
{
type: 'paragraph',
children: [
{
text: 'Hello collaborator!'
}
]
}
]
const PORT = process.env.PORT || 9000
const server = express()
.use(express.static('build'))
.listen(PORT, () => console.log(`Listening on ${PORT}`))
const config = {
entry: server, // or specify port to start io server
defaultValue,
saveFrequency: 2000,
onAuthRequest: async (query, socket) => {
// some query validation
return true
},
onDocumentLoad: async pathname => {
// request initial document ValueJSON by pathnme
return defaultValue
},
onDocumentSave: async (pathname, doc) => {
// save document
// console.log('onDocumentSave', pathname, doc)
}
}
const connection = new AutomergeCollaboration(config)

@ -1,56 +0,0 @@
import React, { useState, useEffect } from 'react'
import faker from 'faker'
import styled from '@emotion/styled'
import Room from './Room'
const App = () => {
const [rooms, setRooms] = useState<string[]>([])
const addRoom = () => setRooms(rooms.concat(faker.lorem.slug(4)))
const removeRoom = (room: string) => () =>
setRooms(rooms.filter(r => r !== room))
useEffect(() => {
addRoom()
}, [])
return (
<div>
<Panel>
<AddButton type="button" onClick={addRoom}>
Add Room
</AddButton>
</Panel>
{rooms.map(room => (
<Room key={room} slug={room} removeRoom={removeRoom(room)} />
))}
</div>
)
}
export default App
const Panel = styled.div`
display: flex;
`
const Button = styled.button`
padding: 6px 14px;
display: block;
outline: none;
font-size: 14px;
max-width: 200px;
text-align: center;
color: palevioletred;
border: 2px solid palevioletred;
`
const AddButton = styled(Button)`
margin-left: 0px;
color: violet;
margin-bottom: 10px;
border: 2px solid violet;
`

@ -1,57 +0,0 @@
import React from 'react'
interface Caret {
color: string
isForward: boolean
name: string
}
const Caret: React.FC<Caret> = ({ color, isForward, name }) => {
const cursorStyles = {
...cursorStyleBase,
background: color,
left: isForward ? '100%' : '0%'
}
const caretStyles = {
...caretStyleBase,
background: color,
left: isForward ? '100%' : '0%'
}
caretStyles[isForward ? 'bottom' : 'top'] = 0
return (
<>
<span contentEditable={false} style={caretStyles}>
<span style={{ position: 'relative' }}>
<span contentEditable={false} style={cursorStyles}>
{name}
</span>
</span>
</span>
</>
)
}
export default Caret
const cursorStyleBase = {
position: 'absolute',
top: -2,
pointerEvents: 'none',
userSelect: 'none',
transform: 'translateY(-100%)',
fontSize: 10,
color: 'white',
background: 'palevioletred',
whiteSpace: 'nowrap'
} as any
const caretStyleBase = {
position: 'absolute',
pointerEvents: 'none',
userSelect: 'none',
height: '1.2em',
width: 2,
background: 'palevioletred'
} as any

@ -1,122 +0,0 @@
import React, { useState, useEffect, useMemo } from 'react'
import { createEditor, Node } from 'slate'
import { withHistory } from 'slate-history'
import { withReact } from 'slate-react'
import randomColor from 'randomcolor'
import styled from '@emotion/styled'
import { withIOCollaboration, useCursor } from '@hiveteams/collab-client'
import { Instance, Title, H4, Button } from './Components'
import EditorFrame from './EditorFrame'
import { withLinks } from './plugins/link'
const defaultValue: Node[] = [
{
type: 'paragraph',
children: [
{
text: ''
}
]
}
]
interface ClientProps {
name: string
id: string
slug: string
removeUser: (id: any) => void
}
const Client: React.FC<ClientProps> = ({ id, name, slug, removeUser }) => {
const [value, setValue] = useState<Node[]>(defaultValue)
const [isOnline, setOnlineState] = useState<boolean>(false)
const color = useMemo(
() =>
randomColor({
luminosity: 'dark',
format: 'rgba',
alpha: 1
}),
[]
)
const editor = useMemo(() => {
const slateEditor = withLinks(withReact(withHistory(createEditor())))
const origin =
process.env.NODE_ENV === 'production'
? window.location.origin
: 'http://localhost:9000'
const options = {
docId: '/' + slug,
cursorData: {
name,
color,
alphaColor: color.slice(0, -2) + '0.2)'
},
url: `${origin}/${slug}`,
connectOpts: {
query: {
name,
token: id,
slug
}
},
onConnect: () => setOnlineState(true),
onDisconnect: () => setOnlineState(false)
}
return withIOCollaboration(slateEditor, options)
}, [])
useEffect(() => {
editor.connect()
return editor.destroy
}, [])
const { decorate } = useCursor(editor)
const toggleOnline = () => {
const { connect, disconnect } = editor
isOnline ? disconnect() : connect()
}
return (
<Instance online={isOnline}>
<Title>
<Head>Editor: {name}</Head>
<div style={{ display: 'flex', marginTop: 10, marginBottom: 10 }}>
<Button type="button" onClick={toggleOnline}>
Go {isOnline ? 'offline' : 'online'}
</Button>
<Button type="button" onClick={() => removeUser(id)}>
Remove
</Button>
</div>
</Title>
<EditorFrame
editor={editor}
value={value}
decorate={decorate}
onChange={(value: Node[]) => setValue(value)}
/>
</Instance>
)
}
export default Client
const Head = styled(H4)`
margin-right: auto;
`

@ -1,93 +0,0 @@
import styled from '@emotion/styled'
export const RoomWrapper = styled.div`
padding-bottom: 10px;
border-bottom: 2px solid #e8e8e8;
`
export const H4 = styled.h4`
margin: 0;
padding-right: 10px;
`
export const Input = styled.input`
padding: 6px 14px;
font-size: 14px;
margin-top: 10px;
margin-bottom: 10px;
min-width: 240px;
outline: none;
border: 2px solid palevioletred;
margin-right: auto;
`
export const Button = styled.button`
padding: 6px 14px;
display: block;
outline: none;
background-color: transparent;
font-size: 14px;
text-align: center;
color: palevioletred;
white-space: nowrap;
border: 2px solid palevioletred;
& + button {
margin-left: 10px;
}
`
export const IconButton = styled(Button)((props: any) => ({
color: props.active ? 'mediumvioletred' : 'lightpink',
border: 'none',
padding: 0
}))
export const Icon = styled.div``
export const Grid = styled.div`
display: grid;
grid-gap: 1vw;
grid-template-columns: 1fr 1fr;
@media (max-width: 767px) {
grid-template-columns: 1fr;
}
`
export const Title = styled.div`
display: flex;
align-items: center;
margin-bottom: 10px;
@media (max-width: 767px) {
flex-wrap: wrap;
}
`
export const Instance = styled.div<{ online: boolean }>`
background: ${props =>
props.online ? 'rgba(128, 128, 128, 0.1)' : 'rgba(247, 0, 0, 0.2)'};
padding: 20px 20px 30px;
`
export const ClientFrame = styled.div`
box-shadow: 2px 2px 4px rgba(128, 128, 128, 0.2);
padding: 10px;
min-height: 70px;
margin-left: -10px;
margin-right: -10px;
background: white;
blockquote {
border-left: 2px solid #ddd;
margin-left: 0;
margin-right: 0;
padding-left: 10px;
color: #aaa;
font-style: italic;
}
a {
color: purple;
text-decoration: none;
}
a:visited {
color: darkmagenta;
}
`

@ -1,189 +0,0 @@
import React, { useCallback } from 'react'
import { Node } from 'slate'
import {
Slate,
ReactEditor,
Editable,
RenderLeafProps,
useSlate
} from 'slate-react'
import { ClientFrame, IconButton, Icon } from './Components'
import Caret from './Caret'
import { isBlockActive, toggleBlock } from './plugins/block'
import { isMarkActive, toggleMark } from './plugins/mark'
import { isLinkActive, insertLink, unwrapLink } from './plugins/link'
export interface EditorFrame {
editor: ReactEditor
value: Node[]
decorate: any
onChange: (value: Node[]) => void
}
const renderElement = (props: any) => <Element {...props} />
const EditorFrame: React.FC<EditorFrame> = ({
editor,
value,
decorate,
onChange
}) => {
const renderLeaf = useCallback((props: any) => <Leaf {...props} />, [
decorate
])
return (
<ClientFrame>
<Slate editor={editor} value={value} onChange={onChange}>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
position: 'sticky',
top: 0,
backgroundColor: 'white',
zIndex: 1
}}
>
<MarkButton format="bold" icon="format_bold" />
<MarkButton format="italic" icon="format_italic" />
<MarkButton format="underline" icon="format_underlined" />
<MarkButton format="code" icon="code" />
<BlockButton format="heading-one" icon="looks_one" />
<BlockButton format="heading-two" icon="looks_two" />
<BlockButton format="block-quote" icon="format_quote" />
<BlockButton format="numbered-list" icon="format_list_numbered" />
<BlockButton format="bulleted-list" icon="format_list_bulleted" />
<LinkButton />
</div>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
decorate={decorate}
/>
</Slate>
</ClientFrame>
)
}
export default EditorFrame
const Element: React.FC<any> = ({ attributes, children, element }) => {
switch (element.type) {
case 'link':
return (
<a {...attributes} href={element.href}>
{children}
</a>
)
case 'block-quote':
return <blockquote {...attributes}>{children}</blockquote>
case 'bulleted-list':
return <ul {...attributes}>{children}</ul>
case 'heading-one':
return <h1 {...attributes}>{children}</h1>
case 'heading-two':
return <h2 {...attributes}>{children}</h2>
case 'list-item':
return <li {...attributes}>{children}</li>
case 'numbered-list':
return <ol {...attributes}>{children}</ol>
default:
return <p {...attributes}>{children}</p>
}
}
const Leaf: React.FC<RenderLeafProps> = ({ attributes, children, leaf }) => {
if (leaf.bold) {
children = <strong>{children}</strong>
}
if (leaf.code) {
children = <code>{children}</code>
}
if (leaf.italic) {
children = <em>{children}</em>
}
if (leaf.underline) {
children = <u>{children}</u>
}
return (
<span
{...attributes}
style={
{
position: 'relative',
backgroundColor: leaf.alphaColor
} as any
}
>
{leaf.isCaret ? <Caret {...(leaf as any)} /> : null}
{children}
</span>
)
}
const BlockButton: React.FC<any> = ({ format, icon }) => {
const editor = useSlate()
return (
<IconButton
active={isBlockActive(editor, format)}
onMouseDown={event => {
event.preventDefault()
toggleBlock(editor, format)
}}
>
<Icon className="material-icons">{icon}</Icon>
</IconButton>
)
}
const MarkButton: React.FC<any> = ({ format, icon }) => {
const editor = useSlate()
return (
<IconButton
active={isMarkActive(editor, format)}
onMouseDown={event => {
event.preventDefault()
toggleMark(editor, format)
}}
>
<Icon className="material-icons">{icon}</Icon>
</IconButton>
)
}
const LinkButton = () => {
const editor = useSlate()
const isActive = isLinkActive(editor)
return (
<IconButton
active={isActive}
onMouseDown={event => {
event.preventDefault()
if (isActive) return unwrapLink(editor)
const url = window.prompt('Enter the URL of the link:')
url && insertLink(editor, url)
}}
>
<Icon className="material-icons">link</Icon>
</IconButton>
)
}

@ -1,73 +0,0 @@
import React, { useState, ChangeEvent } from 'react'
import faker from 'faker'
import debounce from 'lodash/debounce'
import { RoomWrapper, H4, Title, Button, Grid, Input } from './Components'
import Client from './Client'
interface User {
id: string
name: string
}
interface RoomProps {
slug: string
removeRoom: () => void
}
const createUser = (): User => ({
id: faker.random.uuid(),
name: `${faker.name.firstName()} ${faker.name.lastName()}`
})
const Room: React.FC<RoomProps> = ({ slug, removeRoom }) => {
const [users, setUsers] = useState<User[]>([createUser(), createUser()])
const [roomSlug, setRoomSlug] = useState<string>(slug)
const [isRemounted, setRemountState] = useState(false)
const remount = debounce(() => {
setRemountState(true)
setTimeout(setRemountState, 50, false)
}, 300)
const changeSlug = (e: ChangeEvent<HTMLInputElement>) => {
setRoomSlug(e.target.value)
remount()
}
const addUser = () => setUsers(users => users.concat(createUser()))
const removeUser = (userId: string) =>
setUsers(users => users.filter((u: User) => u.id !== userId))
return (
<RoomWrapper>
<Title>
<H4>Document slug:</H4>
<Input type="text" value={roomSlug} onChange={changeSlug} />
<Button type="button" onClick={addUser}>
Add random user
</Button>
<Button type="button" onClick={removeRoom}>
Remove Room
</Button>
</Title>
<Grid>
{users.map((user: User) =>
isRemounted ? null : (
<Client
{...user}
slug={roomSlug}
key={user.id}
removeUser={removeUser}
/>
)
)}
</Grid>
</RoomWrapper>
)
}
export default Room

@ -1,6 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

@ -1,30 +0,0 @@
import { Transforms, Editor } from 'slate'
const LIST_TYPES: string[] = ['numbered-list', 'bulleted-list']
export const toggleBlock = (editor: any, format: any) => {
const isActive = isBlockActive(editor, format)
const isList = LIST_TYPES.includes(format)
Transforms.unwrapNodes(editor, {
match: n => LIST_TYPES.includes(n.type as any),
split: true
})
Transforms.setNodes(editor, {
type: isActive ? 'paragraph' : isList ? 'list-item' : format
})
if (!isActive && isList) {
const block = { type: format, children: [] }
Transforms.wrapNodes(editor, block)
}
}
export const isBlockActive = (editor: any, format: any) => {
const [match] = Editor.nodes(editor, {
match: n => n.type === format
})
return !!match
}

@ -1,73 +0,0 @@
import isUrl from 'is-url'
import { Transforms, Editor, Range } from 'slate'
export interface LinkEditor extends Editor {
insertData: (data: any) => void
}
export const withLinks = <T extends Editor>(editor: T) => {
const e = editor as T & LinkEditor
const { insertData, insertText, isInline } = e
e.isInline = (element: any) => {
return element.type === 'link' ? true : isInline(element)
}
e.insertText = (text: string) => {
if (text && isUrl(text)) {
wrapLink(editor, text)
} else {
insertText(text)
}
}
e.insertData = (data: any) => {
const text = data.getData('text/plain')
if (text && isUrl(text)) {
wrapLink(editor, text)
} else {
insertData(data)
}
}
return editor
}
export const insertLink = (editor: Editor, href: string) => {
if (editor.selection) {
wrapLink(editor, href)
}
}
export const isLinkActive = (editor: Editor) => {
const [link] = Editor.nodes(editor, { match: n => n.type === 'link' })
return !!link
}
export const unwrapLink = (editor: Editor) => {
Transforms.unwrapNodes(editor, { match: n => n.type === 'link' })
}
export const wrapLink = (editor: Editor, href: string) => {
if (isLinkActive(editor)) {
unwrapLink(editor)
}
const { selection } = editor
const isCollapsed = selection && Range.isCollapsed(selection)
const link = {
type: 'link',
href,
children: isCollapsed ? [{ text: href }] : []
}
if (isCollapsed) {
Transforms.insertNodes(editor, link)
} else {
Transforms.wrapNodes(editor, link, { split: true })
Transforms.collapse(editor, { edge: 'end' })
}
}

@ -1,16 +0,0 @@
import { Editor } from 'slate'
export const toggleMark = (editor: Editor, format: any) => {
const isActive = isMarkActive(editor, format)
if (isActive) {
Editor.removeMark(editor, format)
} else {
Editor.addMark(editor, format, true)
}
}
export const isMarkActive = (editor: Editor, format: any) => {
const marks = Editor.marks(editor)
return marks ? marks[format] === true : false
}

@ -1 +0,0 @@
/// <reference types="react-scripts" />

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@hiveteams/collab-bridge": ["../../bridge"],
"@hiveteams/collab-client": ["../../client"]
}
},
"references": [{ "path": "../client" }, { "path": "../backend" }]
}

@ -1,31 +0,0 @@
{
"include": [
"src/**/*"
],
"extends": "./tsconfig.extend.json",
"compilerOptions": {
"rootDir": "../",
"baseUrl": "./src",
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"resolveJsonModule": true,
"declaration": false,
"declarationMap": false,
"noEmit": true,
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react"
}
}

@ -1,29 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./src",
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"resolveJsonModule": true,
"declaration": false,
"declarationMap": false,
"noEmit": true,
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react"
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx"]
}

@ -2,12 +2,7 @@ import * as Automerge from 'automerge'
import { Node } from 'slate'
import {
toCollabAction,
toSync,
SyncDoc,
CollabAction
} from '@hiveteams/collab-bridge'
import { toCollabAction, toSync, SyncDoc, CollabAction } from 'bridge/index'
import { debugCollabBackend } from './utils/debug'
/**

@ -4,7 +4,7 @@ import { Node } from 'slate'
import { Server } from 'http'
import throttle from 'lodash/throttle'
import flatten from 'lodash/flatten'
import { SyncDoc, CollabAction, toJS } from '@hiveteams/collab-bridge'
import { SyncDoc, CollabAction, toJS } from 'bridge/index'
import { debugCollabBackend } from './utils/debug'
import AutomergeBackend from './AutomergeBackend'
import getActiveConnections from './utils/getActiveConnections'

@ -1,6 +1,6 @@
import AutomergeCollaboration, {
IAutomergeMetaData
} from './AutomergeCollaboration'
import getActiveConnections from 'utils/getActiveConnections'
import getActiveConnections from './utils/getActiveConnections'
export { AutomergeCollaboration, IAutomergeMetaData, getActiveConnections }

@ -12,7 +12,7 @@ import {
setCursor,
toSlateOp,
CursorData
} from '@hiveteams/collab-bridge'
} from 'src/bridge/index'
import { AutomergeEditor } from './interfaces'
/**

@ -3,10 +3,13 @@ import { createServer } from 'http'
import fs from 'fs'
import isEqual from 'lodash/isEqual'
import { createEditor, Editor, Element, Node, Transforms } from 'slate'
import { createDoc, SyncDoc, toJS, toSlateOp } from '@hiveteams/collab-bridge'
import AutomergeCollaboration, {
IAutomergeMetaData
} from '@hiveteams/collab-backend/lib/AutomergeCollaboration'
import { createDoc, SyncDoc, toJS, toSlateOp, getTarget } from '../bridge/index'
import {
AutomergeCollaboration,
IAutomergeMetaData,
getActiveConnections
} from '../backend/index'
import withIOCollaboration from './withIOCollaboration'
import {
AutomergeEditor,
@ -14,8 +17,6 @@ import {
SocketIOPluginOptions,
WithSocketIOEditor
} from './interfaces'
import { getTarget } from '@hiveteams/collab-bridge/src/path'
import getActiveConnections from '@hiveteams/collab-backend/src/utils/getActiveConnections'
const connectionSlug = 'test'
const docId = `/${connectionSlug}`
@ -54,13 +55,13 @@ const collabBackend = new AutomergeCollaboration({
entry: server,
defaultValue: defaultSlateJson,
saveFrequency: 1000,
async onAuthRequest(query) {
async onAuthRequest(query: string) {
return { _id: 'test-id', name: 'Eric' }
},
async onDocumentLoad(pathname) {
async onDocumentLoad(pathname: string) {
return defaultSlateJson
},
onTrace(metaData, socket, computationFn) {
onTrace(metaData: any, socket: any, computationFn: () => void) {
operationTraces.push(metaData)
computationFn()
}

@ -0,0 +1,27 @@
// This example is for an Editor with `ReactEditor` and `HistoryEditor`
import { BaseEditor, BaseRange } from 'slate'
import { HistoryEditor } from 'slate-history'
export type CustomEditor = BaseEditor & HistoryEditor
export type CustomElement = {
type?: string
children: CustomText[]
}
export type CustomRange = {
isCaret?: boolean
} & BaseRange
export type FormattedText = { text: string; bold?: true }
export type CustomText = FormattedText
declare module 'slate' {
interface CustomTypes {
Editor: CustomEditor
Element: CustomElement
Text: CustomText
Range: CustomRange
}
}

@ -1,12 +1,12 @@
import Automerge from 'automerge'
import { Editor } from 'slate'
import { CollabAction, CursorData, SyncDoc } from '@hiveteams/collab-bridge'
import { CollabAction, CursorData, SyncDoc } from 'bridge/index'
export interface AutomergeOptions {
docId: string
cursorData?: CursorData
preserveExternalHistory?: boolean
onError?: (msg: string | Error, data: any) => void
onError?: (msg: string | unknown, data: any) => void
}
export interface AutomergeEditor extends Editor {
@ -29,7 +29,7 @@ export interface AutomergeEditor extends Editor {
onCursor: (data: any) => void
handleError: (err: Error | string, data?: any) => void
handleError: (err: unknown | string, data?: any) => void
}
export interface SocketIOPluginOptions {

@ -2,7 +2,7 @@ import { useState, useCallback, useEffect, useMemo } from 'react'
import { Text, Range, Path, NodeEntry } from 'slate'
import { toJS, Cursor, Cursors } from '@hiveteams/collab-bridge'
import { toJS, Cursor, Cursors } from 'bridge/index'
import useMounted from './useMounted'
import { AutomergeEditor } from './interfaces'

@ -4,7 +4,7 @@ import { Editor } from 'slate'
import { AutomergeConnector } from './automerge-connector'
import { CollabAction } from '@hiveteams/collab-bridge'
import { CollabAction } from 'bridge/index'
import {
AutomergeEditor,
AutomergeOptions,
@ -31,7 +31,7 @@ const withAutomerge = <T extends Editor>(
* Helper function for handling errors
*/
editor.handleError = (err: Error | string, data: any = {}) => {
editor.handleError = (err: unknown | string, data: any = {}) => {
const { onError } = options
if (onError) {
onError(err, data)

@ -1,7 +1,7 @@
import io from 'socket.io-client'
import Automerge from 'automerge'
import { CollabAction } from '@hiveteams/collab-bridge'
import { CollabAction } from 'bridge/index'
import {
AutomergeEditor,
AutomergeOptions,

@ -1,7 +1,7 @@
{
"compilerOptions": {
"rootDir": ".",
"baseUrl": "./packages",
"rootDir": "./src",
"baseUrl": "./src",
"lib": ["dom", "dom.iterable", "esnext"],
"allowSyntheticDefaultImports": true,
"declaration": true,
Loading…
Cancel
Save