Merge lp:~rockstar/phazr/editor into lp:phazr

Proposed by Paul Hummer
Status: Merged
Approved by: Paul Hummer
Approved revision: 20
Merged at revision: 8
Proposed branch: lp:~rockstar/phazr/editor
Merge into: lp:phazr
Diff against target: 479 lines (+430/-1)
6 files modified
assets/cancel.svg (+90/-0)
assets/close.svg (+90/-0)
assets/ok.svg (+90/-0)
examples/editableplugin/index.html (+70/-0)
src/css/phazr.css (+5/-1)
src/js/editableplugin/editableplugin.js (+85/-0)
To merge this branch: bzr merge lp:~rockstar/phazr/editor
Reviewer Review Type Date Requested Status
Deryck Hodge Pending
Review via email: mp+59869@code.launchpad.net

Commit message

Add inline editor plugin

Description of the change

This branch creates a Node plugin that makes any text node editable. Try out the example.

One thing it doesn't do currently is provide buttons for save/cancel. On the single line editor, you can hit enter. The multiline editor doesn't provide a way to save just yet. If it did, at least there's a way to handle it.

I'm pushing it now to get opinions on implementing this as a plugin rather than a full blown widget.

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) wrote :

I like this as a plugin very much. I see no downside to it really. And the code is simple and straightforward.

We absolutely need to add cancel/save icons for the multiline version. Or maybe make this an option on the initializer?

Also, the real neat trick of a text editor is that the underlying form element mimics the layout of the text on the page. I think this is the hard trick and my gut says we need some form of this in phazr. Call sites will override it, I'm sure, but we need some sense of how to do this in the plugin itself.

As a start, this is very good though!

Revision history for this message
Paul Hummer (rockstar) wrote :

Finally got around to fixing this. There are new graphics (and the SVG versions of them as well). I'm trying to get some better graphics out of design. When that happens, then I'll replace them again. The other images were just too horrible. At least these are simple.

As always, test it out, tell me what to fix.

Revision history for this message
Deryck Hodge (deryck) wrote :

Some thoughts at a quick poke....

The graphics look nice. I see why you want nice ones, but these aren't bad as generic ones.

In terms of the widget itself:

* The text still reflows to input element, rather than preserving the look of the page
* Linebreaks are ignored in multi-line when adding the text back to the DOM

For this last one, we rely on html response back from the server to update the DOM correctly. but that always felt wrong to me. It seems like the widget ought to be responsible for updating the DOM correctly. What do you think?

Not sure what you consider important for release. I think the first point above is very important, but we can talk more offline about that.

Revision history for this message
Paul Hummer (rockstar) wrote :

I need to figure out what's going on with the line break thing. I don't want to do too many hackery things to it, especially if someone would prefer to bounce it off the server (maybe we should escape HTML, but a XSS against yourself seems a bit silly.

I think the reflow issue needs to be left up to the implementer to fix. I'm not sure if anyone is even going to use this, and if they do, I don't want the CSS to be so specific that it makes their lives harder (and thus eliminating what phazr is really for).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'assets'
2=== added file 'assets/cancel.svg'
3--- assets/cancel.svg 1970-01-01 00:00:00 +0000
4+++ assets/cancel.svg 2011-05-28 21:21:33 +0000
5@@ -0,0 +1,90 @@
6+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
7+<!-- Created with Inkscape (http://www.inkscape.org/) -->
8+
9+<svg
10+ xmlns:dc="http://purl.org/dc/elements/1.1/"
11+ xmlns:cc="http://creativecommons.org/ns#"
12+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
13+ xmlns:svg="http://www.w3.org/2000/svg"
14+ xmlns="http://www.w3.org/2000/svg"
15+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
16+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
17+ width="128"
18+ height="128"
19+ id="svg2985"
20+ version="1.1"
21+ inkscape:version="0.48.1 r9760"
22+ sodipodi:docname="close.svg">
23+ <defs
24+ id="defs2987" />
25+ <sodipodi:namedview
26+ id="base"
27+ pagecolor="#ffffff"
28+ bordercolor="#666666"
29+ borderopacity="1.0"
30+ inkscape:pageopacity="0.0"
31+ inkscape:pageshadow="2"
32+ inkscape:zoom="2.8284271"
33+ inkscape:cx="61.841334"
34+ inkscape:cy="80.280491"
35+ inkscape:current-layer="g3788"
36+ showgrid="true"
37+ inkscape:document-units="px"
38+ inkscape:grid-bbox="true"
39+ inkscape:window-width="1280"
40+ inkscape:window-height="702"
41+ inkscape:window-x="0"
42+ inkscape:window-y="0"
43+ inkscape:window-maximized="0" />
44+ <metadata
45+ id="metadata2990">
46+ <rdf:RDF>
47+ <cc:Work
48+ rdf:about="">
49+ <dc:format>image/svg+xml</dc:format>
50+ <dc:type
51+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
52+ <dc:title></dc:title>
53+ </cc:Work>
54+ </rdf:RDF>
55+ </metadata>
56+ <g
57+ id="layer1"
58+ inkscape:label="Layer 1"
59+ inkscape:groupmode="layer"
60+ transform="translate(0,64)">
61+ <path
62+ sodipodi:type="arc"
63+ style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
64+ id="path2993"
65+ sodipodi:cx="63.090908"
66+ sodipodi:cy="64"
67+ sodipodi:rx="55.454544"
68+ sodipodi:ry="54.18182"
69+ d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z"
70+ transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" />
71+ <g
72+ id="g3788"
73+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)">
74+ <rect
75+ ry="3"
76+ rx="4.5"
77+ y="-50"
78+ x="54"
79+ height="100"
80+ width="15"
81+ id="rect3763"
82+ style="fill:#a40000;fill-opacity:1;stroke:none" />
83+ <rect
84+ transform="matrix(0,1,-1,0,0,0)"
85+ style="fill:#a40000;fill-opacity:1;stroke:none"
86+ id="rect3786"
87+ width="15"
88+ height="100"
89+ x="-7.5"
90+ y="-111.5"
91+ rx="4.5"
92+ ry="3" />
93+ </g>
94+ </g>
95+</svg>
96
97=== added file 'assets/close.svg'
98--- assets/close.svg 1970-01-01 00:00:00 +0000
99+++ assets/close.svg 2011-05-28 21:21:33 +0000
100@@ -0,0 +1,90 @@
101+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
102+<!-- Created with Inkscape (http://www.inkscape.org/) -->
103+
104+<svg
105+ xmlns:dc="http://purl.org/dc/elements/1.1/"
106+ xmlns:cc="http://creativecommons.org/ns#"
107+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
108+ xmlns:svg="http://www.w3.org/2000/svg"
109+ xmlns="http://www.w3.org/2000/svg"
110+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
111+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
112+ width="128"
113+ height="128"
114+ id="svg2985"
115+ version="1.1"
116+ inkscape:version="0.48.1 r9760"
117+ sodipodi:docname="close.svg">
118+ <defs
119+ id="defs2987" />
120+ <sodipodi:namedview
121+ id="base"
122+ pagecolor="#ffffff"
123+ bordercolor="#666666"
124+ borderopacity="1.0"
125+ inkscape:pageopacity="0.0"
126+ inkscape:pageshadow="2"
127+ inkscape:zoom="2.8284271"
128+ inkscape:cx="57.068363"
129+ inkscape:cy="80.280491"
130+ inkscape:current-layer="g3788"
131+ showgrid="true"
132+ inkscape:document-units="px"
133+ inkscape:grid-bbox="true"
134+ inkscape:window-width="1280"
135+ inkscape:window-height="702"
136+ inkscape:window-x="0"
137+ inkscape:window-y="0"
138+ inkscape:window-maximized="0" />
139+ <metadata
140+ id="metadata2990">
141+ <rdf:RDF>
142+ <cc:Work
143+ rdf:about="">
144+ <dc:format>image/svg+xml</dc:format>
145+ <dc:type
146+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
147+ <dc:title></dc:title>
148+ </cc:Work>
149+ </rdf:RDF>
150+ </metadata>
151+ <g
152+ id="layer1"
153+ inkscape:label="Layer 1"
154+ inkscape:groupmode="layer"
155+ transform="translate(0,64)">
156+ <path
157+ sodipodi:type="arc"
158+ style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
159+ id="path2993"
160+ sodipodi:cx="63.090908"
161+ sodipodi:cy="64"
162+ sodipodi:rx="55.454544"
163+ sodipodi:ry="54.18182"
164+ d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z"
165+ transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" />
166+ <g
167+ id="g3788"
168+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)">
169+ <rect
170+ ry="3"
171+ rx="4.5"
172+ y="-50"
173+ x="54"
174+ height="100"
175+ width="15"
176+ id="rect3763"
177+ style="fill:#646464;fill-opacity:1;stroke:none" />
178+ <rect
179+ transform="matrix(0,1,-1,0,0,0)"
180+ style="fill:#646464;fill-opacity:1;stroke:none"
181+ id="rect3786"
182+ width="15"
183+ height="100"
184+ x="-7.5"
185+ y="-111.5"
186+ rx="4.5"
187+ ry="3" />
188+ </g>
189+ </g>
190+</svg>
191
192=== added file 'assets/ok.svg'
193--- assets/ok.svg 1970-01-01 00:00:00 +0000
194+++ assets/ok.svg 2011-05-28 21:21:33 +0000
195@@ -0,0 +1,90 @@
196+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
197+<!-- Created with Inkscape (http://www.inkscape.org/) -->
198+
199+<svg
200+ xmlns:dc="http://purl.org/dc/elements/1.1/"
201+ xmlns:cc="http://creativecommons.org/ns#"
202+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
203+ xmlns:svg="http://www.w3.org/2000/svg"
204+ xmlns="http://www.w3.org/2000/svg"
205+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
206+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
207+ width="128"
208+ height="128"
209+ id="svg2985"
210+ version="1.1"
211+ inkscape:version="0.48.1 r9760"
212+ sodipodi:docname="ok.svg">
213+ <defs
214+ id="defs2987" />
215+ <sodipodi:namedview
216+ id="base"
217+ pagecolor="#ffffff"
218+ bordercolor="#666666"
219+ borderopacity="1.0"
220+ inkscape:pageopacity="0.0"
221+ inkscape:pageshadow="2"
222+ inkscape:zoom="2.8284271"
223+ inkscape:cx="68.165161"
224+ inkscape:cy="54.161237"
225+ inkscape:current-layer="g3788"
226+ showgrid="true"
227+ inkscape:document-units="px"
228+ inkscape:grid-bbox="true"
229+ inkscape:window-width="1280"
230+ inkscape:window-height="702"
231+ inkscape:window-x="0"
232+ inkscape:window-y="0"
233+ inkscape:window-maximized="0" />
234+ <metadata
235+ id="metadata2990">
236+ <rdf:RDF>
237+ <cc:Work
238+ rdf:about="">
239+ <dc:format>image/svg+xml</dc:format>
240+ <dc:type
241+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
242+ <dc:title></dc:title>
243+ </cc:Work>
244+ </rdf:RDF>
245+ </metadata>
246+ <g
247+ id="layer1"
248+ inkscape:label="Layer 1"
249+ inkscape:groupmode="layer"
250+ transform="translate(0,64)">
251+ <path
252+ sodipodi:type="arc"
253+ style="fill:none;fill-rule:evenodd;stroke:#646464;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
254+ id="path2993"
255+ sodipodi:cx="63.090908"
256+ sodipodi:cy="64"
257+ sodipodi:rx="55.454544"
258+ sodipodi:ry="54.18182"
259+ d="M 118.54545,64 A 55.454544,54.18182 0 1 1 7.636364,64 55.454544,54.18182 0 1 1 118.54545,64 z"
260+ transform="matrix(1.0721871,0,0,1.0973726,-3.6452576,-70.231844)" />
261+ <g
262+ id="g3788"
263+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,20.695964,43.304036)">
264+ <rect
265+ style="fill:#00a400;fill-opacity:1;stroke:none"
266+ id="rect3896"
267+ width="15"
268+ height="40"
269+ x="22.212006"
270+ y="-26.36396"
271+ rx="3"
272+ ry="3" />
273+ <rect
274+ ry="2.7"
275+ rx="3"
276+ transform="matrix(0,1,-1,0,0,0)"
277+ y="-112.58459"
278+ x="-1.437555"
279+ height="90"
280+ width="15"
281+ id="rect3898"
282+ style="fill:#00a400;fill-opacity:1;stroke:none" />
283+ </g>
284+ </g>
285+</svg>
286
287=== added directory 'examples/editableplugin'
288=== added file 'examples/editableplugin/index.html'
289--- examples/editableplugin/index.html 1970-01-01 00:00:00 +0000
290+++ examples/editableplugin/index.html 2011-05-28 21:21:33 +0000
291@@ -0,0 +1,70 @@
292+<!DOCTYPE html>
293+<meta charset="utf-8" />
294+<html>
295+ <head>
296+ <title>Editable Plugin</title>
297+ <script type="text/javascript"
298+ src="http://yui.yahooapis.com/3.3.0/build/yui/yui-min.js"></script>
299+ <script type="text/javascript"
300+ src="../../src/js/editableplugin/editableplugin.js"></script>
301+ <link rel="stylesheet" href="../../src/css/phazr.css" />
302+ </head>
303+ <body>
304+ <div id="editableplugin-demo">
305+ <h1>Editable Plugin</h1>
306+ <p>EditablePlugin allows any text Node to be editable. Select the Node
307+ and plug Editable into it.</p>
308+ <p>
309+ <span id="editable-demo">
310+ This text has a typo, but it's compeletely editable.
311+ </span>
312+ </p>
313+ <script type="text/javascript">
314+ YUI().use('editableplugin', 'node', function(Y) {
315+ var demo = Y.one('#editable-demo');
316+ demo.plug(Y.Editable, {
317+ saveFn: function(value) { alert(value); }
318+ });
319+ });
320+ </script>
321+ <pre><code>
322+YUI().use('editableplugin', 'node', function(Y) {
323+ var demo = Y.one('#editable-demo');
324+ demo.plug(Y.Editable, {
325+ saveFn: function(value) { alert(value); }
326+ });
327+});
328+ </code></pre>
329+
330+ <h2>Multi-line editing</h2>
331+ <p>By default, the editor only supports a single line. In some cases,
332+ you may want to edit paragraphs of text. In this case, you can simply
333+ pass <code>multiline: true</code> when you plug the editor into the
334+ Node.</p>
335+ <p>
336+ <p id="multiline-editable-demo">
337+ This is a paragraph of text. There are no typos in this paragraph, but you may want to edit it anyway. Go ahead and try it.
338+ </p>
339+ </p>
340+ <script type="text/javascript">
341+ YUI().use('editableplugin', 'node', function(Y) {
342+ var demo = Y.one('#multiline-editable-demo');
343+ demo.plug(Y.Editable, {
344+ multiline: true,
345+ saveFn: function(value) { alert(value); }
346+ });
347+ });
348+ </script>
349+ <pre><code>
350+YUI().use('editableplugin', 'node', function(Y) {
351+ var demo = Y.one('#multiline-editable-demo');
352+ demo.plug(Y.Editable, {
353+ multiline: true,
354+ saveFn: function(value) { alert(value); }
355+ });
356+});
357+ </code></pre>
358+
359+ </div>
360+ </body>
361+</html>
362
363=== modified file 'src/css/images/cancel.png'
364Binary files src/css/images/cancel.png 2011-03-23 02:17:50 +0000 and src/css/images/cancel.png 2011-05-28 21:21:33 +0000 differ
365=== modified file 'src/css/images/close.png'
366Binary files src/css/images/close.png 2011-03-23 02:17:50 +0000 and src/css/images/close.png 2011-05-28 21:21:33 +0000 differ
367=== renamed file 'src/css/images/okay.png' => 'src/css/images/ok.png'
368Binary files src/css/images/okay.png 2011-03-23 02:17:50 +0000 and src/css/images/ok.png 2011-05-28 21:21:33 +0000 differ
369=== modified file 'src/css/phazr.css'
370--- src/css/phazr.css 2011-04-07 05:41:04 +0000
371+++ src/css/phazr.css 2011-05-28 21:21:33 +0000
372@@ -1,3 +1,7 @@
373+a.save { background: url('images/ok.png') 0 0 no-repeat; display: inline-block; height: 16px; width: 16px; }
374+a.cancel { background: url('images/cancel.png') 0 0 no-repeat; display: inline-block; height: 16px; width: 16px; }
375+
376+
377 div.yui3-widget .clickable {
378 cursor: pointer;
379 }
380@@ -41,7 +45,7 @@
381 width: 32px;
382 }
383 div.yui3-overlay div.yui3-overlay-controls a#save-control {
384- background: url('images/okay.png') 0 0 no-repeat;
385+ background: url('images/ok.png') 0 0 no-repeat;
386 }
387 div.yui3-overlay div.yui3-overlay-controls a#cancel-control {
388 background: url('images/cancel.png') 0 0 no-repeat;
389
390=== added directory 'src/js/editableplugin'
391=== added file 'src/js/editableplugin/editableplugin.js'
392--- src/js/editableplugin/editableplugin.js 1970-01-01 00:00:00 +0000
393+++ src/js/editableplugin/editableplugin.js 2011-05-28 21:21:33 +0000
394@@ -0,0 +1,85 @@
395+YUI.add('editableplugin', function(Y) {
396+
397+ Y.Editable = Y.Base.create('editable', Y.Plugin.Base, [], {
398+ initializer: function(config) {
399+ var host = this.get('host');
400+ host.addClass('editable');
401+ host.on('click', Y.bind(this.edit, this));
402+ },
403+ cancel: function() {
404+ var editor = this.get('editor'),
405+ host = this.get('host');
406+ editor.hide();
407+ host.show();
408+ },
409+ edit: function(e) {
410+ e.preventDefault();
411+ var host = this.get('host');
412+ multiline = this.get('multiline'),
413+ text = Y.Lang.trim(host.get('text')),
414+ editor = this.get('editor');
415+
416+ if (editor === null) {
417+ var parentNode = host.get('parentNode'),
418+ editor = Y.Node.create('<div></div>'),
419+ controls = Y.Node.create('<span></span'),
420+ save = Y.Node.create('<a class="clickable save"></a>'),
421+ cancel = Y.Node.create('<a class="clickable cancel"></a>'),
422+ textbox = null;
423+
424+ if (multiline) {
425+ textbox = Y.Node.create('<textarea></textarea>');
426+ } else {
427+ textbox = Y.Node.create('<input type="text" />');
428+ }
429+
430+ save.on('click', this.save, this);
431+ cancel.on('click', this.cancel, this);
432+
433+ controls.appendChild(save);
434+ controls.appendChild(cancel);
435+ editor.appendChild(textbox);
436+ editor.appendChild(controls);
437+ editor.hide();
438+
439+ textbox.on('keyup', this._handleKeyPress, this);
440+ parentNode.insert(editor, host);
441+ this.set('editor', editor);
442+ }
443+
444+ editor.get('firstChild').set('value', text);
445+ editor.show();
446+ host.hide();
447+ },
448+ save: function() {
449+ var editor = this.get('editor'),
450+ host = this.get('host'),
451+ save = this.get('saveFn'),
452+ value = editor.get('firstChild').get('value');
453+ if (save !== null) { save(value); }
454+ host.setContent(value);
455+ editor.hide();
456+ host.show();
457+ },
458+ _handleKeyPress: function(e) {
459+ if (e.keyCode) {
460+ if (e.keyCode === 27) { this.cancel(); }
461+ if (e.keyCode === 13 && this.get('multiline') === false) {
462+ this.save();
463+ }
464+ }
465+ }
466+ }, {
467+ ATTRS: {
468+ editor: { value: null },
469+ multiline: { value: false },
470+ saveFn: { value: null }
471+ },
472+ NS: 'editable'
473+ });
474+
475+}, '1.0', { requires: [
476+ 'base',
477+ 'node',
478+ 'plugin',
479+]});

Subscribers

People subscribed via source and target branches

to all changes: