diff --git a/config.js b/config.js index 1672ce6..72bdd4e 100644 --- a/config.js +++ b/config.js @@ -1,5 +1,5 @@ 'use strict'; module.exports = { - examples: ['inline-nodes', 'code-editor', 'collabwriter', 'form', 'focused', 'images', 'inception', 'input', 'macros', 'nested', 'tables'] + examples: ['code-editor', 'collabwriter', 'form', 'focused', 'images', 'inception', 'inline-nodes', 'inline-property', 'input', 'macros', 'nested', 'tables'] }; \ No newline at end of file diff --git a/index.html b/index.html index 1a98a5f..98a3e4b 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@

Substance Examples

  • Text Macros
  • Code-Editor
  • Nested Element
  • +
  • Inline Property Editor
  • Inception
  • Collabwriter
  • diff --git a/inline-property/README.md b/inline-property/README.md new file mode 100644 index 0000000..71e802c --- /dev/null +++ b/inline-property/README.md @@ -0,0 +1,4 @@ +# Inline Property Node + +An inline node that mimicks UX of an annotation, +i.e. its content is edited as it would belong to the text, instead of using an overlay. diff --git a/inline-property/app.js b/inline-property/app.js new file mode 100644 index 0000000..fd08b2e --- /dev/null +++ b/inline-property/app.js @@ -0,0 +1,7 @@ +'use strict'; + +var proseExample = require('../lib/proseExample'); +var fixture = require('./fixture'); +var config = require('./config'); + +proseExample(fixture, config); diff --git a/inline-property/app.scss b/inline-property/app.scss new file mode 100644 index 0000000..2967c37 --- /dev/null +++ b/inline-property/app.scss @@ -0,0 +1,6 @@ +$fa-font-path: "../fonts" !default; +@import '../node_modules/font-awesome/scss/font-awesome'; + +body { + overflow: hidden; +} diff --git a/inline-property/config.js b/inline-property/config.js new file mode 100644 index 0000000..91f885e --- /dev/null +++ b/inline-property/config.js @@ -0,0 +1,13 @@ +'use strict'; + +var ProseEditorPackage = require('substance/packages/prose-editor/ProseEditorPackage'); +var InlinePropertyPackage = require('./inline-property/package'); + +module.exports = { + name: 'inline-property-example', + configure: function(config) { + config.import(ProseEditorPackage); + config.import(InlinePropertyPackage); + config.addStyle(__dirname+'/app.scss'); + } +}; diff --git a/inline-property/fixture.js b/inline-property/fixture.js new file mode 100644 index 0000000..519ca78 --- /dev/null +++ b/inline-property/fixture.js @@ -0,0 +1,42 @@ +'use strict'; +/* eslint-disable indent */ + +module.exports = function(tx) { + var body = tx.get('body'); + + tx.create({ + id: 'title', + type: 'heading', + level: 1, + content: 'Inline Property Editor' + }); + body.show('title'); + + tx.create({ + id: 'intro', + type: 'paragraph', + content: [ + "This shows an $, which renders a property editor", + "in the flow of the text, creating an interface similar to a regular annotations.", + "Still, in contrast to an annotation the content is owned by the inline node." + ].join(' ') + }); + tx.create({ + type: 'inline-property', + id: 'i1', + path: ['intro', 'content'], + startOffset: 14, + endOffset: 15, + content: 'inline property', + }); + body.show('intro'); + + tx.create({ + id: 'the-end', + type: 'paragraph', + content: [ + "That's it." + ].join('') + }); + body.show('the-end'); +}; diff --git a/inline-property/index.html b/inline-property/index.html new file mode 100644 index 0000000..4b612a1 --- /dev/null +++ b/inline-property/index.html @@ -0,0 +1,11 @@ + + + + Inline Property Editor + + + + + + + diff --git a/inline-property/inline-property/InlineProperty.js b/inline-property/inline-property/InlineProperty.js new file mode 100644 index 0000000..cb106ad --- /dev/null +++ b/inline-property/inline-property/InlineProperty.js @@ -0,0 +1,32 @@ +'use strict'; + +var InlineNode = require('substance/model/InlineNode'); + +function InlineProperty() { + InlineProperty.super.apply(this, arguments); +} + +InlineProperty.Prototype = function() { + + this.getEvaluatedValue = function() { + var result; + try { + result = window.eval(this.value); // eslint-disable-line no-eval + } catch (err) { + console.error(err); + result = "ERROR"; + } + return result; + }; + +}; + +InlineNode.extend(InlineProperty); + +InlineProperty.static.name = 'inline-property'; + +InlineProperty.static.defineSchema({ + content: { type: 'text', default: ' ' }, +}); + +module.exports = InlineProperty; diff --git a/inline-property/inline-property/InlinePropertyCommand.js b/inline-property/inline-property/InlinePropertyCommand.js new file mode 100644 index 0000000..26089ce --- /dev/null +++ b/inline-property/inline-property/InlinePropertyCommand.js @@ -0,0 +1,44 @@ +'use strict'; + +var InlineNodeCommand = require('substance/ui/InlineNodeCommand'); +var InlineProperty = require('./InlineProperty'); + +function InlinePropertyCommand() { + InlinePropertyCommand.super.apply(this, arguments); +} + +InlinePropertyCommand.Prototype = function() { + + this.getCommandState = function(props, context) { + var sel = context.documentSession.getSelection(); + var newState = { + disabled: true, + active: false, + node: undefined + }; + if (!sel || sel.isNull() || !sel.isPropertySelection()) { + return newState; + } + newState.disabled = false; + var doc = context.documentSession.getDocument(); + var node = doc.get(sel.path[0]); + if (node && node.type === 'inline-property') { + newState.active = true; + newState.node = node; + } else { + var annos = this._getAnnotationsForSelection(props, context); + if (annos.length === 1 && annos[0].getSelection().equals(sel)) { + newState.active = true; + newState.node = annos[0]; + } + } + return newState; + }; + +}; + +InlineNodeCommand.extend(InlinePropertyCommand); + +InlinePropertyCommand.static.name = InlineProperty.static.name; + +module.exports = InlinePropertyCommand; diff --git a/inline-property/inline-property/InlinePropertyComponent.js b/inline-property/inline-property/InlinePropertyComponent.js new file mode 100644 index 0000000..672dee8 --- /dev/null +++ b/inline-property/inline-property/InlinePropertyComponent.js @@ -0,0 +1,44 @@ +'use strict'; + +var InlineNodeComponent = require('substance/ui/InlineNodeComponent'); +var TextPropertyEditor = require('substance/ui/TextPropertyEditor'); + +function InlinePropertyComponent() { + InlinePropertyComponent.super.apply(this, arguments); +} + +InlinePropertyComponent.Prototype = function() { + + var _super = InlinePropertyComponent.super.prototype; + + this.didMount = function() { + _super.didMount.call(this); + this.props.node.on('content:changed', this.rerender, this); + }; + + this.dispose = function() { + _super.dispose.call(this); + this.props.node.off(this); + }; + + this.getClassNames = function() { + // ATTENTION: ATM it is necessary to add .sc-inline-node + return 'sc-inline-property sc-inline-node'; + }; + + this.renderContent = function($$) { + var node = this.props.node; + var el = $$(TextPropertyEditor, { + disabled: this.isDisabled(), + tagName: 'span', + path: [node.id, 'content'], + withoutBreak: true + }).ref('content'); + return el; + }; + +}; + +InlineNodeComponent.extend(InlinePropertyComponent); + +module.exports = InlinePropertyComponent; diff --git a/inline-property/inline-property/InlinePropertyTool.js b/inline-property/inline-property/InlinePropertyTool.js new file mode 100644 index 0000000..9514d11 --- /dev/null +++ b/inline-property/inline-property/InlinePropertyTool.js @@ -0,0 +1,14 @@ +'use strict'; + +var AnnotationTool = require('substance/ui/AnnotationTool'); +var InlinePropertyCommand = require('./InlinePropertyCommand'); + +function InlinePropertyTool() { + InlinePropertyTool.super.apply(this, arguments); +} + +AnnotationTool.extend(InlinePropertyTool); + +InlinePropertyTool.static.name = InlinePropertyCommand.static.name; + +module.exports = InlinePropertyTool; diff --git a/inline-property/inline-property/_inline-property.scss b/inline-property/inline-property/_inline-property.scss new file mode 100644 index 0000000..acd9093 --- /dev/null +++ b/inline-property/inline-property/_inline-property.scss @@ -0,0 +1,6 @@ +.sc-inline-property { + > .se-container { + padding: 2px 4px; + background: #eee; + } +} \ No newline at end of file diff --git a/inline-property/inline-property/package.js b/inline-property/inline-property/package.js new file mode 100644 index 0000000..5ad7dfc --- /dev/null +++ b/inline-property/inline-property/package.js @@ -0,0 +1,18 @@ +'use strict'; + +var InlineProperty = require('./InlineProperty'); +var InlinePropertyComponent = require('./InlinePropertyComponent'); +var InlinePropertyCommand = require('./InlinePropertyCommand'); +var InlinePropertyTool = require('./InlinePropertyTool'); + +module.exports = { + name: 'hybrid-inline', + configure: function(config) { + config.addNode(InlineProperty); + config.addComponent(InlineProperty.static.name, InlinePropertyComponent); + config.addCommand(InlinePropertyCommand); + config.addTool(InlinePropertyTool); + config.addIcon(InlinePropertyCommand.static.name, { 'fontawesome': 'fa-cube' }); + config.addStyle(__dirname+'/_inline-property.scss'); + } +}; diff --git a/server.js b/server.js index 0d1f4b1..292098e 100644 --- a/server.js +++ b/server.js @@ -9,10 +9,9 @@ var server = require('substance/util/server'); var config = require('./config'); config.examples.forEach(function(folder) { - - var exampleConfigPath = path.join(__dirname, folder, 'config'); server.serveStyles(app, '/'+folder+'/app.css', { - configPath: exampleConfigPath + scssPath: path.join(__dirname, folder, 'app.scss'), + configPath: path.join(__dirname, folder, 'config') }); server.serveJS(app, '/'+folder+'/app.js', path.join(__dirname, folder, 'app.js')); });