git.iko.soy/rowtype-yoga/purescript-tiptap
on main help sign in
Add tiptap ffi
commitc7512a6d9cb25f631806eb3156c069a119bc25c5 authorsigma-andex <77549848+sigma-andex@users.noreply.github.com> dateMon 20 Mar 2023, 19:28 (4 years ago) parentd894ff4
16 files changed+248
.gitignore+10 LICENSE+16 README.md+2 package.json+12 packages.dhall+18 spago.dhall+16 src/TipTap/Collaboration.js+6 src/TipTap/Collaboration.purs+14 src/TipTap/CollaborationCursor.js+13 src/TipTap/CollaborationCursor.purs+30 src/TipTap/React.js+17 src/TipTap/React.purs+59 src/TipTap/StarterKit.js+6 src/TipTap/StarterKit.purs+13 src/TipTap/TipTap.purs+5 test/Main.purs+11
.gitignorenew file+10
@@ -0,0 +1,10 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/generated-docs/
6+/.psc-package/
7+/.psc*
8+/.purs*
9+/.psa*
10+/.spago
LICENSEnew file+16
@@ -0,0 +1,16 @@
1+MIT No Attribution
2+
3+Copyright 2021 Betalyra Sociedade Unipessoal Lda
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy of this
6+software and associated documentation files (the "Software"), to deal in the Software
7+without restriction, including without limitation the rights to use, copy, modify,
8+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+permit persons to whom the Software is furnished to do so.
10+
11+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md+2
@@ -1 +1,3 @@
11# purescript-tiptap
2+
3+Purescript FFI bindings for [tiptap](https://tiptap.dev/).
package.jsonnew file+12
@@ -0,0 +1,12 @@
1+{
2+ "name": "purescript-tiptap",
3+ "version": "1.0.0",
4+ "description": "",
5+ "main": "index.js",
6+ "scripts": {
7+ "test": "echo \"Error: no test specified\" && exit 1"
8+ },
9+ "keywords": [],
10+ "author": "",
11+ "license": "ISC"
12+}
packages.dhallnew file+18
@@ -0,0 +1,18 @@
1+let upstream =
2+ https://github.com/purescript/package-sets/releases/download/psc-0.15.7-20230320/packages.dhall
3+ sha256:6f2a4b50b793f304d3a64fd25d631de990de280981c73b0683a090e4fa499f0d
4+
5+in upstream
6+ with yjs =
7+ { dependencies =
8+ [ "console"
9+ , "effect"
10+ , "foreign"
11+ , "functions"
12+ , "newtype"
13+ , "prelude"
14+ , "web-dom"
15+ ]
16+ , repo = "https://github.com/rowtype-yoga/purescript-yjs.git"
17+ , version = "fc233e7"
18+ }
spago.dhallnew file+16
@@ -0,0 +1,16 @@
1+{ name = "tiptap"
2+, dependencies =
3+ [ "aff"
4+ , "console"
5+ , "effect"
6+ , "maybe"
7+ , "nullable"
8+ , "prelude"
9+ , "react-basic-hooks"
10+ , "typelevel-prelude"
11+ , "web-html"
12+ , "yjs"
13+ ]
14+, packages = ./packages.dhall
15+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
16+}
src/TipTap/Collaboration.jsnew file+6
@@ -0,0 +1,6 @@
1+import * as collab from '@tiptap/extension-collaboration'
2+
3+export const collaboration = collab.default
4+export const configure = props => c => {
5+ return c.configure(props)
6+}
src/TipTap/Collaboration.pursnew file+14
@@ -0,0 +1,14 @@
1+module TipTap.Collaboration where
2+
3+import Prelude
4+
5+import TipTap.TipTap (Extension)
6+import Yjs.Yjs as Yjs
7+
8+foreign import data Collaboration :: Type
9+
10+foreign import collaboration :: Collaboration
11+
12+type Props = { fragment :: Yjs.YXmlFragment }
13+
14+foreign import configure :: Props -> Collaboration -> Extension
src/TipTap/CollaborationCursor.jsnew file+13
@@ -0,0 +1,13 @@
1+import * as collabcursor from "@tiptap/extension-collaboration-cursor";
2+
3+export const collaborationCursor = collabcursor.default;
4+export const configureImpl =
5+ ({ provider, user }) =>
6+ (render) =>
7+ (c) => {
8+ if (render != null) {
9+ return c.configure({ provider, user, render });
10+ } else {
11+ return c.configure({ provider, user });
12+ }
13+ };
src/TipTap/CollaborationCursor.pursnew file+30
@@ -0,0 +1,30 @@
1+module TipTap.CollaborationCursor
2+ ( CollaborationCursor
3+
4+ , Props
5+ , collaborationCursor
6+ , configure
7+ ) where
8+
9+import Prelude
10+
11+import Data.Maybe (Maybe)
12+import Data.Nullable (Nullable)
13+import Data.Nullable as Nullable
14+import Effect (Effect)
15+import Effect.Aff.Compat (EffectFn1, mkEffectFn1)
16+import TipTap.TipTap (Extension)
17+import Type.Function (type ($))
18+import Web.HTML (HTMLElement)
19+import Yjs.Yjs as Yjs
20+
21+foreign import data CollaborationCursor :: Type
22+
23+foreign import collaborationCursor :: CollaborationCursor
24+
25+type Props user = { provider :: Yjs.Provider, user :: user }
26+
27+foreign import configureImpl :: forall user. Props user -> Nullable $ EffectFn1 user HTMLElement -> CollaborationCursor -> Extension
28+
29+configure ∷ ∀ (user ∷ Type). Props user → Maybe (user -> Effect HTMLElement) → CollaborationCursor → Extension
30+configure props maybeRender = configureImpl props $ maybeRender <#> mkEffectFn1 # Nullable.toNullable
src/TipTap/React.jsnew file+17
@@ -0,0 +1,17 @@
1+
2+import * as tiptap from "@tiptap/react";
3+
4+
5+export const useEditorImpl = (options) => tiptap.useEditor(options)
6+
7+export const editorContent = tiptap.EditorContent
8+
9+export const bubbleMenu = tiptap.BubbleMenu
10+
11+export const chainImpl = (input) => input.chain()
12+export const focusImpl = (input) => input.focus()
13+export const toggleItalicImpl = (input) => input.toggleItalic()
14+export const toggleBoldImpl = (input) => input.toggleBold()
15+export const toggleStrikeImpl = (input) => input.toggleStrike()
16+
17+export const run = (chainedCommand) => () => chainedCommand.run()
src/TipTap/React.pursnew file+59
@@ -0,0 +1,59 @@
1+module TipTap.React where
2+
3+import Prelude
4+
5+import Data.Maybe (Maybe)
6+import Data.Nullable (Nullable)
7+import Data.Nullable as Nullable
8+import Effect (Effect)
9+import Effect.Aff.Compat (EffectFn1, runEffectFn1)
10+import React.Basic.Hooks (Hook, JSX, ReactComponent, unsafeHook)
11+import TipTap.TipTap (Extension)
12+
13+foreign import data Editor :: Type
14+
15+type Props = { extensions :: Array Extension }
16+
17+foreign import useEditorImpl :: EffectFn1 Props (Nullable Editor)
18+
19+foreign import data UseEditor :: Type -> Type
20+
21+useEditor ∷ Props -> Hook (UseEditor) (Maybe Editor)
22+useEditor props = unsafeHook $ runEffectFn1 useEditorImpl props <#> Nullable.toMaybe
23+
24+foreign import editorContent :: ReactComponent { editor :: Editor }
25+
26+type TippyOptions = { duration :: Int }
27+
28+foreign import bubbleMenu :: ReactComponent { children :: Array JSX, editor :: Editor, tippyOptions :: TippyOptions }
29+
30+foreign import data ChainedCommands :: Type
31+
32+class ChainedCommandsBuilder input output | input -> output where
33+ chain :: input -> Effect output
34+ focus :: input -> Effect output
35+ toggleBold :: input -> Effect output
36+ toggleItalic :: input -> Effect output
37+ toggleStrike :: input -> Effect output
38+
39+foreign import chainImpl :: forall input output. EffectFn1 input output
40+foreign import focusImpl :: forall input output. EffectFn1 input output
41+foreign import toggleBoldImpl :: forall input output. EffectFn1 input output
42+foreign import toggleItalicImpl :: forall input output. EffectFn1 input output
43+foreign import toggleStrikeImpl :: forall input output. EffectFn1 input output
44+
45+instance ChainedCommandsBuilder Editor ChainedCommands where
46+ chain = runEffectFn1 chainImpl
47+ focus = runEffectFn1 focusImpl
48+ toggleBold = runEffectFn1 toggleBoldImpl
49+ toggleItalic = runEffectFn1 toggleItalicImpl
50+ toggleStrike = runEffectFn1 toggleStrikeImpl
51+
52+instance ChainedCommandsBuilder ChainedCommands ChainedCommands where
53+ chain = runEffectFn1 chainImpl
54+ focus = runEffectFn1 focusImpl
55+ toggleBold = runEffectFn1 toggleBoldImpl
56+ toggleItalic = runEffectFn1 toggleItalicImpl
57+ toggleStrike = runEffectFn1 toggleStrikeImpl
58+
59+foreign import run :: ChainedCommands -> Effect Boolean
src/TipTap/StarterKit.jsnew file+6
@@ -0,0 +1,6 @@
1+
2+import * as sk from '@tiptap/starter-kit';
3+
4+export const starterKit = sk.default
5+
6+export const configure = props => s => s.configure(props)
src/TipTap/StarterKit.pursnew file+13
@@ -0,0 +1,13 @@
1+module TipTap.StarterKit where
2+
3+import Prelude
4+
5+import TipTap.TipTap (Extension)
6+
7+foreign import data StarterKit :: Type
8+
9+foreign import starterKit :: StarterKit
10+
11+type Props = { history :: Boolean }
12+
13+foreign import configure :: Props -> StarterKit -> Extension
src/TipTap/TipTap.pursnew file+5
@@ -0,0 +1,5 @@
1+module TipTap.TipTap where
2+
3+import Prelude
4+
5+foreign import data Extension :: Type
test/Main.pursnew file+11
@@ -0,0 +1,11 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Class.Console (log)
7+
8+main :: Effect Unit
9+main = do
10+ log "🍝"
11+ log "You should add some tests."