├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── package.json └── src ├── actions └── index.js ├── containers └── App.js ├── index.html ├── index.js ├── lovutils.js ├── reducers └── index.js └── standalone.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-runtime", "transform-strict-mode"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "browser": true 6 | }, 7 | "ecmaFeatures": { 8 | "jsx": true, 9 | "experimentalObjectRestSpread": true 10 | }, 11 | "plugins": [ 12 | "react" 13 | ], 14 | "extends": "eslint:recommended", 15 | "rules": { 16 | "comma-dangle": 0, 17 | "curly": [2, "multi-line"], 18 | "eol-last": 2, 19 | "jsx-quotes": 2, 20 | "no-console": 1, 21 | "no-fallthrough": 0, 22 | "no-multiple-empty-lines": [2, {"max": 1}], 23 | "no-trailing-spaces": [2], 24 | "no-use-before-define": [2, "nofunc"], 25 | "semi": [2, "never"] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | lib/ 3 | node_modules/ 4 | standalone/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | An example of using [react-jsonld-editor](https://github.com/editorsnotes/react-jsonld-editor) with dynamically loaded vocabuaries from [Linked Open Vocabularies](http://lov.okfn.org/dataset/lov/): 2 | 3 | 1. First, add some vocabularies by typing their names into the autocompleting input. 4 | 1. Then, use the classes and properties from those vocabularies to create a valid JSON-LD object. 5 | 1. When you're done, save it to a file. 6 | 7 | [Try it out.](https://editorsnotes.github.io/edit-with-lov/) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edit-with-lov", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Demo of editing JSON-LD with LOV vocabs", 6 | "main": "lib/index.js", 7 | "files": "lib", 8 | "scripts": { 9 | "lint": "eslint src", 10 | "transpile": "babel src -d lib --source-maps inline", 11 | "prebuild": "npm -s run lint", 12 | "build": "npm -s run transpile", 13 | "precss": "mkdir -p lib", 14 | "css": "cat node_modules/react-jsonld-editor/src/style.css node_modules/highlight.js/styles/default.css > lib/style.css", 15 | "predevelop": "npm -s run css", 16 | "develop": "budo src/standalone.js --css lib/style.css --live -- -t [ babelify --sourceMaps inline ]", 17 | "prestandalone": "npm -s run build && npm -s run css && mkdir -p standalone", 18 | "standalone": "NODE_ENV=production browserify lib/standalone.js | uglifyjs -c > standalone/bundle.js", 19 | "poststandalone": "cp src/index.html lib/style.css standalone && cd standalone && zip ../standalone.zip *" 20 | }, 21 | "author": "Ryan Shaw", 22 | "license": "ISC", 23 | "devDependencies": { 24 | "babel-cli": "^6.1.2", 25 | "babel-plugin-transform-runtime": "^6.1.2", 26 | "babel-plugin-transform-strict-mode": "^6.8.0", 27 | "babel-preset-es2015": "^6.1.2", 28 | "babelify": "^7.2.0", 29 | "budo": "^8.0.4", 30 | "eslint": "^2.12.0", 31 | "uglify": "^0.1.5" 32 | }, 33 | "dependencies": { 34 | "file-saver": "^1.3.3", 35 | "highland": "^2.8.1", 36 | "highlight.js": "^9.7.0", 37 | "immutable": "^3.8.1", 38 | "n3": "^0.4.5", 39 | "rdf-ns": "0.0.2", 40 | "react": "^15.2.1", 41 | "react-dom": "^15.2.1", 42 | "react-hyperscript": "^2.4.0", 43 | "react-jsonld-editor": "^5.1.2", 44 | "react-lowlight": "^1.0.2", 45 | "react-redux": "^4.4.5", 46 | "rebass": "^0.4.0-beta.8", 47 | "redux": "^3.5.2", 48 | "redux-thunk": "^2.1.0", 49 | "reselect": "^2.5.3" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | const {getVocabularies, getVocabulary} = require('../lovutils') 2 | 3 | const UPDATE_INPUT = 'UPDATE_INPUT' 4 | const UPDATE_SUGGESTIONS = 'UPDATE_SUGGESTIONS' 5 | const REQUEST_VOCABS = 'REQUEST_VOCABS' 6 | const RECEIVE_VOCABS = 'RECEIVE_VOCABS' 7 | const REQUEST_VOCAB = 'REQUEST_VOCAB' 8 | const RECEIVE_VOCAB = 'RECEIVE_VOCAB' 9 | const RECEIVE_ERROR = 'RECEIVE_ERROR' 10 | const UPDATE_NODE = 'UPDATE_NODE' 11 | 12 | const updateInput = input => ( 13 | { type: UPDATE_INPUT 14 | , input 15 | } 16 | ) 17 | 18 | const updateSuggestions = suggestions => ( 19 | { type: UPDATE_SUGGESTIONS 20 | , suggestions 21 | } 22 | ) 23 | 24 | const requestVocabs = () => ( 25 | { type: REQUEST_VOCABS } 26 | ) 27 | 28 | const receiveVocabs = list => ( 29 | { type: RECEIVE_VOCABS 30 | , list 31 | , receivedAt: Date.now() 32 | } 33 | ) 34 | 35 | const requestVocab = vocab => ( 36 | { type: REQUEST_VOCAB 37 | , vocab 38 | } 39 | ) 40 | 41 | const receiveVocab = (vocab, {info, classes, properties}) => ( 42 | { type: RECEIVE_VOCAB 43 | , vocab 44 | , info 45 | , classes 46 | , properties 47 | , receivedAt: Date.now() 48 | } 49 | ) 50 | 51 | const receiveError = error => ( 52 | { type: RECEIVE_ERROR 53 | , error 54 | , receivedAt: Date.now() 55 | } 56 | ) 57 | 58 | const fetchVocab = vocab => dispatch => { 59 | dispatch(requestVocab(vocab)) 60 | return getVocabulary(vocab) 61 | .then(o => dispatch(receiveVocab(vocab, o))) 62 | .catch(error => dispatch(receiveError(error))) 63 | } 64 | 65 | const fetchVocabs = () => dispatch => { 66 | dispatch(requestVocabs()) 67 | return getVocabularies() 68 | .then(list => dispatch(receiveVocabs(list))) 69 | .catch(error => dispatch(receiveError(error))) 70 | } 71 | 72 | const updateNode = node => ( 73 | { type: UPDATE_NODE 74 | , node: node 75 | } 76 | ) 77 | 78 | module.exports = 79 | { UPDATE_INPUT 80 | , UPDATE_SUGGESTIONS 81 | , REQUEST_VOCABS 82 | , RECEIVE_VOCABS 83 | , REQUEST_VOCAB 84 | , RECEIVE_VOCAB 85 | , RECEIVE_ERROR 86 | , UPDATE_NODE 87 | , updateInput 88 | , updateSuggestions 89 | , requestVocabs 90 | , receiveVocabs 91 | , requestVocab 92 | , receiveVocab 93 | , receiveError 94 | , fetchVocab 95 | , fetchVocabs 96 | , updateNode 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/containers/App.js: -------------------------------------------------------------------------------- 1 | const React = require('react') // eslint-disable-line no-unused-vars 2 | , h = require('react-hyperscript') 3 | , {connect} = require('react-redux') 4 | , {bindActionCreators} = require('redux') 5 | , {Block, Heading, List, Button} = require('rebass') 6 | , Editor = require('react-jsonld-editor') 7 | , Autosuggest = require('rebass-autosuggest') 8 | , Lowlight = require('react-lowlight') 9 | , {saveAs} = require('file-saver') 10 | , { updateInput 11 | , updateSuggestions 12 | , updateNode 13 | , fetchVocab 14 | } = require('../actions') 15 | 16 | Lowlight.registerLanguage('json', require('highlight.js/lib/languages/json')) 17 | 18 | const App = ( 19 | { input 20 | , suggestions 21 | , getSuggestions 22 | , vocabularies 23 | , node 24 | , classes 25 | , properties 26 | , updateInput 27 | , updateSuggestions 28 | , updateNode 29 | , fetchVocab 30 | }) => ( 31 | 32 | h(Block, 33 | [ h('p', 34 | [ 'An example of using ' 35 | , h('a', {href: 'https://github.com/editorsnotes/react-jsonld-editor'}, 36 | 'react-jsonld-editor') 37 | , ' with dynamically loaded vocabuaries from ' 38 | , h('a', {href: 'http://lov.okfn.org'}, 'Linked Open Vocabularies') 39 | , '. First, add some vocabularies by typing their names into the input below. Then, use the classes and properties from those vocabularies to create a JSON-LD object.' 40 | ]) 41 | , h('p', 42 | [ 'See the ' 43 | , h('a', {href: 'https://github.com/editorsnotes/edit-with-lov'}, 44 | 'source') 45 | , ' for this demo.' 46 | ]) 47 | 48 | , vocabularies.isEmpty() 49 | ? h(Heading, {size: 4, mb: 1}, ['No vocabularies loaded']) 50 | : h(Block, 51 | [ h(Heading, {size: 4, mb: 1}, ['Loaded vocabularies:']) 52 | , h(List, vocabularies 53 | .map(({title, url}) => h( 54 | 'a', {href: url, target: '_blank'}, title)) 55 | .toArray() 56 | ) 57 | ]) 58 | 59 | , h(Autosuggest, 60 | { name: 'vocabulary' 61 | , label: 'Add vocabulary' 62 | , hideLabel: true 63 | , suggestions 64 | , onSuggestionsFetchRequested: 65 | ({value}) => updateSuggestions(getSuggestions(value)) 66 | , onSuggestionsClearRequested: 67 | () => updateSuggestions([]) 68 | , getSuggestionValue: 69 | suggestion => suggestion.label 70 | , renderSuggestion: 71 | suggestion => suggestion.label 72 | , onSuggestionSelected: 73 | (e, {suggestion}) => fetchVocab(suggestion.id) 74 | , inputProps: 75 | { value: input 76 | , placeholder: 77 | 'Type the name of a vocabulary to add here, e.g. FOAF' 78 | , onChange: (e, {newValue, method}) => { 79 | if (method === 'type') { updateInput(newValue) } 80 | } 81 | } 82 | } 83 | ) 84 | 85 | , ...(vocabularies.isEmpty() 86 | ? [] 87 | : [ h(Editor, 88 | { node 89 | , classes 90 | , properties 91 | , onSave: node => updateNode(node) 92 | }) 93 | , h(Lowlight, 94 | { language: 'json' 95 | , value: JSON.stringify(node.toJS(), null, 2) 96 | }) 97 | , h(Button, 98 | {onClick: () => saveAs( 99 | new Blob([JSON.stringify(node.toJS(), null, 2)], 100 | {type: 'text/plain;charset=utf-8'}), 101 | 'exported.json', true) 102 | }, 'Save') 103 | ]) 104 | ] 105 | ) 106 | ) 107 | 108 | const matches = (inputValue, inputLength) => prefix => prefix 109 | ? prefix.toLowerCase().slice(0, inputLength) === inputValue 110 | : false 111 | 112 | const getSuggester = vocabs => input => { 113 | const inputValue = String(input).trim().toLowerCase() 114 | const inputLength = inputValue.length 115 | const matchesInput = matches(inputValue, inputLength) 116 | return inputLength === 0 117 | ? [] 118 | : vocabs 119 | .filter(vocab => ( 120 | matchesInput(vocab.prefix) || 121 | matchesInput(vocab.titles[0].value))) 122 | .map(vocab => ( 123 | { id: vocab.uri 124 | , label: vocab.titles[0].value 125 | })) 126 | .toArray() 127 | } 128 | 129 | const mapStateToProps = (state) => ( 130 | { input: state.input 131 | , suggestions: state.suggestions 132 | , getSuggestions: getSuggester(state.availableVocabs) 133 | , vocabularies: 134 | state.loadedVocabs 135 | .valueSeq() 136 | .filterNot(vocab => vocab.isFetching) 137 | .map(vocab => ( 138 | { title: vocab.info.get('titles').first().get('value') 139 | , url: vocab.info.get('uri') 140 | } 141 | )) 142 | , node: state.node 143 | , classes: state.classes 144 | , properties: state.properties 145 | } 146 | ) 147 | 148 | const mapDispatchToProps = dispatch => bindActionCreators( 149 | {updateInput, updateSuggestions, updateNode, fetchVocab}, dispatch) 150 | 151 | module.exports = connect(mapStateToProps, mapDispatchToProps)(App) 152 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |