mirror of
https://github.com/cudr/slate-collaborative.git
synced 2024-10-27 20:34:06 +00:00
Merge pull request #5 from hiveteams/chore/slate-update
Chore/slate update
This commit is contained in:
commit
66e779df2d
@ -3,6 +3,7 @@
|
||||
"plugins": [
|
||||
"@babel/proposal-class-properties",
|
||||
"@babel/proposal-object-rest-spread",
|
||||
"@babel/plugin-proposal-optional-chaining"
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-transform-runtime"
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
76
package.json
76
package.json
@ -1,23 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"description": "Slate collaborative plugin & microservice",
|
||||
"name": "@hiveteams/collab",
|
||||
"version": "1.0.3",
|
||||
"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",
|
||||
"clean": "rm -rf ./build",
|
||||
"build": "./node_modules/typescript/bin/tsc -p ./tsconfig.json",
|
||||
"deploy": "npm run clean && npm run build && cp package.json ./build && npm publish build"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"author": "cudr",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@ -32,19 +23,62 @@
|
||||
},
|
||||
"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/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.6.0",
|
||||
"@babel/preset-typescript": "^7.6.0",
|
||||
"@commitlint/cli": "^9.0.1",
|
||||
"@commitlint/config-conventional": "^9.0.1",
|
||||
"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": {
|
||||
"@types/jest": "^24.9.0",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/react": "^16.12.0",
|
||||
"@types/socket.io": "^2.1.4",
|
||||
"@types/socket.io-client": "1.4.32",
|
||||
"@types/debug": "^4.1.7",
|
||||
"automerge": "0.14.0",
|
||||
"debug": "^4.3.3",
|
||||
"lodash": "4.17.21",
|
||||
"react": "16.12.0",
|
||||
"slate": "0.73.1",
|
||||
"slate-history": "0.66.0",
|
||||
"socket.io": "^2.3.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"moduleDirectories": [
|
||||
"node_modules",
|
||||
"src"
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"babelConfig": ".babelrc",
|
||||
"tsConfig": "tsconfig.json"
|
||||
}
|
||||
},
|
||||
"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 © 2019–2020, [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,53 +0,0 @@
|
||||
{
|
||||
"name": "@hiveteams/collab-backend",
|
||||
"version": "0.7.30",
|
||||
"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:module",
|
||||
"build:module": "yarn run build:types && yarn 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"
|
||||
},
|
||||
"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,3 +0,0 @@
|
||||
import AutomergeCollaboration from './AutomergeCollaboration'
|
||||
|
||||
export { AutomergeCollaboration }
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["./src/**/*"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"baseUrl": "./src",
|
||||
"outDir": "./lib",
|
||||
"composite": true,
|
||||
"paths": {
|
||||
"@hiveteams/collab-bridge": ["../../collab-bridge"]
|
||||
}
|
||||
},
|
||||
"references": [{ "path": "../bridge" }]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
The MIT License
|
||||
|
||||
Copyright © 2019–2020, [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,68 +0,0 @@
|
||||
{
|
||||
"name": "@hiveteams/collab-bridge",
|
||||
"version": "0.7.27",
|
||||
"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:types && yarn 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": "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,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["./src/**/*"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"composite": 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 © 2019–2020, [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
packages/example/src/react-app-env.d.ts
vendored
1
packages/example/src/react-app-env.d.ts
vendored
@ -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'
|
||||
@ -94,13 +94,13 @@ export default class AutomergeCollaboration {
|
||||
/**
|
||||
* Construct error data and call onError callback
|
||||
*/
|
||||
private handleError(socket: SocketIO.Socket, err: Error, data: any = {}) {
|
||||
private handleError(socket: SocketIO.Socket, err: unknown, data: any = {}) {
|
||||
const { id } = socket
|
||||
const { name: docId } = socket.nsp
|
||||
|
||||
if (this.options.onError) {
|
||||
const document = this.backend.getDocument(docId)
|
||||
this.options.onError(err, {
|
||||
this.options.onError(err as Error, {
|
||||
user: this.userMap[id],
|
||||
docId,
|
||||
automergeDocument: document ? Automerge.save(document) : null,
|
6
src/backend/index.ts
Normal file
6
src/backend/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import AutomergeCollaboration, {
|
||||
IAutomergeMetaData
|
||||
} from './AutomergeCollaboration'
|
||||
import getActiveConnections from './utils/getActiveConnections'
|
||||
|
||||
export { AutomergeCollaboration, IAutomergeMetaData, getActiveConnections }
|
@ -1,4 +1,4 @@
|
||||
import { Operation, Range } from 'slate'
|
||||
import { BaseSetNodeOperation, Operation, Range } from 'slate'
|
||||
|
||||
import { CursorData } from '../model'
|
||||
import { toJS } from '../utils'
|
||||
@ -15,7 +15,9 @@ export const setCursor = (
|
||||
|
||||
if (!doc.cursors) doc.cursors = {}
|
||||
|
||||
const newCursor = cursorOps[cursorOps.length - 1]?.newProperties || {}
|
||||
const newCursor =
|
||||
(cursorOps[cursorOps.length - 1] as BaseSetNodeOperation)
|
||||
?.newProperties || {}
|
||||
|
||||
if (selection) {
|
||||
const newCursorData = Object.assign(
|
@ -1,3 +1,4 @@
|
||||
export * from './path'
|
||||
export * from './apply'
|
||||
export * from './convert'
|
||||
export * from './utils'
|
@ -1,8 +1,8 @@
|
||||
import { Element, Node, Path } from 'slate'
|
||||
import { Element, Path } from 'slate'
|
||||
|
||||
import { SyncValue } from '../model'
|
||||
|
||||
export const isTree = (node: Node): boolean => Boolean(node?.children)
|
||||
export const isTree = (node: Element): boolean => Boolean(node?.children)
|
||||
|
||||
export const getTarget = (doc: SyncValue | Element, path: Path) => {
|
||||
const iterate = (current: any, idx: number) => {
|
@ -12,7 +12,7 @@ import {
|
||||
setCursor,
|
||||
toSlateOp,
|
||||
CursorData
|
||||
} from '@hiveteams/collab-bridge'
|
||||
} from '../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()
|
||||
}
|
27
src/client/custom-slate-types.ts
Normal file
27
src/client/custom-slate-types.ts
Normal file
@ -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?: boolean }
|
||||
|
||||
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,21 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"baseUrl": "./packages",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "esnext"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["**/*.ts", "**/*.tsx"]
|
||||
}
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"baseUrl": "./src",
|
||||
"outDir": "./build",
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"exclude": ["node_modules", "build"],
|
||||
"include": ["**/*.ts", "**/*.tsx"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user