diff --git a/TODO.rst b/TODO.rst index b3917a07..27002121 100644 --- a/TODO.rst +++ b/TODO.rst @@ -1,7 +1,8 @@ TODO ==== -- Live search. +- Main menu display children. +- Custom root node in example. - Referencebrowser widget. - Treibstoff styles. - Yafowil bootstrap 5 styles. diff --git a/examples/cone.example/src/cone/example/model.py b/examples/cone.example/src/cone/example/model.py index 34e6408e..6b8ec56d 100644 --- a/examples/cone.example/src/cone/example/model.py +++ b/examples/cone.example/src/cone/example/model.py @@ -232,6 +232,7 @@ def search(self, request, query): result.append({ 'value': md.title, 'target': make_url(request, node=child), - 'icon': md.icon + 'icon': md.icon, + 'description': md.description, }) return result diff --git a/js/src/livesearch.js b/js/src/livesearch.js index e9af2164..8ed226e1 100644 --- a/js/src/livesearch.js +++ b/js/src/livesearch.js @@ -17,10 +17,8 @@ export class LiveSearch { constructor(elem) { this.elem = elem; this.target = `${elem.data('search-target')}/livesearch`; - this.result = $('
+ ${item.description === undefined ? '' : item.description} +
++ ${data.length} Results +
+ `, this.result); + for (const item of data) { + this.render_suggestion(item); + } + } + this.result.tsajax(); + } + on_keydown(evt) { if (evt.keyCode === 13) { return; @@ -99,24 +125,4 @@ export class LiveSearch { this.search(); }, this._delay); } - - on_result(data, status, request) { - console.log(data); - } - - on_select(evt, suggestion, dataset) { - if (!suggestion.target) { - console.log('No suggestion target defined.'); - return; - } - ts.ajax.trigger( - 'contextchanged', - '#layout', - suggestion.target - ); - } - - render_suggestion(suggestion) { - return `${suggestion.value}`; - } } diff --git a/src/cone/app/browser/ajax.py b/src/cone/app/browser/ajax.py index 3a00e6f6..2e603de9 100644 --- a/src/cone/app/browser/ajax.py +++ b/src/cone/app/browser/ajax.py @@ -1,7 +1,6 @@ from cone.app.browser.actions import ActionContext from cone.app.browser.utils import bdajax_warning from cone.app.browser.utils import format_traceback -from cone.app.interfaces import ILiveSearch from cone.tile import render_tile from node.utils import safe_encode from pyramid.exceptions import Forbidden @@ -363,11 +362,3 @@ def render_ajax_form(model, request, name): error='true' ) return Response(rendered) - - -@view_config(name='livesearch', accept='application/json', renderer='json') -def livesearch(model, request): - adapter = request.registry.queryAdapter(model, ILiveSearch) - if not adapter: - return list() - return adapter.search(request, request.params['term']) diff --git a/src/cone/app/browser/layout.py b/src/cone/app/browser/layout.py index 673b5a44..97b6dff3 100644 --- a/src/cone/app/browser/layout.py +++ b/src/cone/app/browser/layout.py @@ -39,12 +39,6 @@ class LogoTile(Tile): """ -@tile(name='livesearch', path='templates/livesearch.pt', permission='login') -class LivesearchTile(Tile): - """Tile rendering the live search. - """ - - @tile(name='colortoggler', path='templates/colortoggler.pt', permission='login') class ColorTogglerTile(Tile): """Tile rendering the color mode toggler. diff --git a/src/cone/app/browser/resources.py b/src/cone/app/browser/resources.py index d48b3daf..97c1751a 100644 --- a/src/cone/app/browser/resources.py +++ b/src/cone/app/browser/resources.py @@ -59,22 +59,6 @@ compressed='bootstrap-icons.min.css' )) -# typeahead -typeahead_resources = wr.ResourceGroup( - name='cone.app-typeahead', - directory=os.path.join(resources_dir, 'typeahead'), - path='typeahead' -) -# typeahead_resources.add(wr.ScriptResource( -# name='typeahead-js', -# depends='jquery-js', -# resource='typeahead.bundle.js' -# )) -# typeahead_resources.add(wr.StyleResource( -# name='typeahead-css', -# resource='typeahead.css' -# )) - # cone cone_resources = wr.ResourceGroup( name='cone.app-cone', @@ -151,7 +135,6 @@ def initialize(cls, config, settings): # register default resources config.register_resource(jquery_resources) config.register_resource(bootstrap_resources) - config.register_resource(typeahead_resources) config.register_resource(cone_resources) def register_resource(self, config, resource): diff --git a/src/cone/app/browser/search.py b/src/cone/app/browser/search.py new file mode 100644 index 00000000..fd1b99fe --- /dev/null +++ b/src/cone/app/browser/search.py @@ -0,0 +1,17 @@ +from cone.app.interfaces import ILiveSearch +from cone.tile import tile +from cone.tile import Tile +from pyramid.view import view_config + + +@tile(name='livesearch', path='templates/livesearch.pt', permission='login') +class LivesearchTile(Tile): + """Tile rendering the live search.""" + + +@view_config(name='livesearch', accept='application/json', renderer='json') +def livesearch(model, request): + adapter = request.registry.queryAdapter(model, ILiveSearch) + if not adapter: + return list() + return adapter.search(request, request.params['term']) diff --git a/src/cone/app/browser/static/cone/cone.app.js b/src/cone/app/browser/static/cone/cone.app.js index 81ac25f5..8463cea1 100644 --- a/src/cone/app/browser/static/cone/cone.app.js +++ b/src/cone/app/browser/static/cone/cone.app.js @@ -319,10 +319,8 @@ var cone = (function (exports, $, ts) { constructor(elem) { this.elem = elem; this.target = `${elem.data('search-target')}/livesearch`; - this.result = $('+ ${item.description === undefined ? '' : item.description} +
++ ${data.length} Results +
+ `, this.result); + for (const item of data) { + this.render_suggestion(item); + } + } + this.result.tsajax(); + } on_keydown(evt) { if (evt.keyCode === 13) { return; @@ -375,23 +418,6 @@ var cone = (function (exports, $, ts) { this.search(); }, this._delay); } - on_result(data, status, request) { - console.log(data); - } - on_select(evt, suggestion, dataset) { - if (!suggestion.target) { - console.log('No suggestion target defined.'); - return; - } - ts.ajax.trigger( - 'contextchanged', - '#layout', - suggestion.target - ); - } - render_suggestion(suggestion) { - return `${suggestion.value}`; - } } class GlobalEvents extends ts.Events { diff --git a/src/cone/app/browser/static/cone/cone.app.min.js b/src/cone/app/browser/static/cone/cone.app.min.js index 307da826..33bbfa72 100644 --- a/src/cone/app/browser/static/cone/cone.app.min.js +++ b/src/cone/app/browser/static/cone/cone.app.min.js @@ -1 +1 @@ -var cone=function(e,t,s){"use strict";class i{constructor(e,t){this.elem=e,this.name=t}set_filter(e){let t=this.elem,i=s.ajax.parse_target(t.attr("ajax:target")),r=t.attr("ajax:event");if(i.params[this.name]=e,t.attr("ajax:path")){let a=t.attr("ajax:path-event");a||(a=r),s.ajax.path({path:i.path+i.query+"&"+this.name+"="+e,event:a,target:i})}let a=r.split(":");s.ajax.trigger({name:a[0],selector:a[1],target:i})}}class r extends i{static initialize(e,s=".batched_items_slice_size select"){t(s,e).each((function(){new r(t(this))}))}constructor(e){super(e,"size"),e.off("change").on("change",this.change_handle.bind(this))}change_handle(e){let s=t("option:selected",this.elem).first();this.set_filter(s.val())}}class a extends i{static initialize(e,s=".batched_items_filter input",i="term"){t(s,e).each((function(){new a(t(this),i)}))}constructor(e,t){super(e,t),e.off("focus").on("focus",this.focus_handle.bind(this)),e.off("keypress").on("keypress",this.keypress_handle.bind(this)),e.off("keyup").on("keyup",this.keyup_handle.bind(this)),e.off("change").on("change",this.change_handle.bind(this))}focus_handle(e){let t=this.elem;t.hasClass("empty_filter")&&(t.val(""),t.removeClass("empty_filter"))}keypress_handle(e){13==e.keyCode&&e.preventDefault()}keyup_handle(e){13==e.keyCode&&(e.preventDefault(),this.set_filter(this.elem.val()))}change_handle(e){e.preventDefault(),this.set_filter(this.elem.val())}}class o{static get media_query(){return window.matchMedia("(prefers-color-scheme: dark)")}static get stored_theme(){return localStorage.getItem("cone-app-color-theme")}static set stored_theme(e){localStorage.setItem("cone-app-color-theme",e)}static get preferred_theme(){return this.stored_theme?this.stored_theme:this.media_query.matches?"dark":"light"}static watch(e){this.media_query.addEventListener("change",e)}static set_theme(e){const t=document.documentElement;"auto"===e&&this.media_query.matches?t.setAttribute("data-bs-theme","dark"):t.setAttribute("data-bs-theme",e)}constructor(){this.bind(),o.set_theme(o.preferred_theme)}bind(){o.watch((()=>{const e=this.stored_theme;"light"===e&&"dark"===e||o.set_theme(o.preferred_theme)}))}}class n extends s.ChangeListener{static initialize(e){const t=s.query_elem("#colortoggle-switch",e);t&&new n(t)}constructor(e){super({elem:e}),this.update(),o.watch((()=>{this.update()}))}update(){const e=o.preferred_theme,t=this.elem,s=t.is(":checked");"dark"!==e||s?"light"===e&&s&&t.prop("checked",!1):t.prop("checked",!0)}on_change(){const e=this.elem.is(":checked")?"dark":"light";o.set_theme(e),o.stored_theme=e}}class l{static initialize(e){new l(e)}constructor(e){this.cut_cookie="cone.app.copysupport.cut",this.copy_cookie="cone.app.copysupport.copy",this.context=e,this.paste_action=t("a#toolbaraction-paste",e),this.paste_action.off("click").on("click",this.handle_paste.bind(this)),this.copyable=t("table tr.selectable.copysupportitem",e),this.copyable.length&&(this.cut_action=t("a#toolbaraction-cut",e),this.cut_action.off("click").on("click",this.handle_cut.bind(this)),this.copy_action=t("a#toolbaraction-copy",e),this.copy_action.off("click").on("click",this.handle_copy.bind(this)),this.selectable=this.copyable.selectable({on_firstclick:this.on_firstclick.bind(this),on_select:this.on_select.bind(this)}).data("selectable"),this.read_selected_from_cookie(this.cut_cookie,"copysupport_cut"),this.read_selected_from_cookie(this.copy_cookie,""))}on_firstclick(e,t){}on_select(e){}write_selected_to_cookie(e){let i=t(this.selectable.selected),r=new Array;i.each((function(){r.push(t(this).attr("ajax:target"))}));let a=r.join("::");s.create_cookie(e,a),a.length?t(this.paste_action).removeClass("disabled"):t(this.paste_action).addClass("disabled")}read_selected_from_cookie(e,i){let r=s.read_cookie(e);if(!r)return;let a,o,n=r.split("::"),l=this;t("table tr.selectable",this.context).each((function(){a=t(this),o=a.attr("ajax:target");for(let e in n)if(n[e]==o){a.addClass("selected"),i&&a.addClass(i),l.selectable.add(a.get(0));break}}))}handle_cut(e){e.preventDefault(),s.create_cookie(this.copy_cookie,"",0),this.write_selected_to_cookie(this.cut_cookie),this.copyable.removeClass("copysupport_cut"),t(this.selectable.selected).addClass("copysupport_cut")}handle_copy(e){e.preventDefault(),s.create_cookie(this.cut_cookie,"",0),this.write_selected_to_cookie(this.copy_cookie),this.copyable.removeClass("copysupport_cut")}handle_paste(e){e.preventDefault();let i=t(e.currentTarget);if(i.hasClass("disabled"))return;let r=s.ajax.parse_target(i.attr("ajax:target"));s.ajax.action({name:"paste",mode:"NONE",selector:"NONE",url:r.url,params:r.params})}}let h={shift_down:!1,ctrl_down:!1};class c{constructor(){t(window).on("keydown",this.key_down.bind(this)),t(window).on("keyup",this.key_up.bind(this))}key_down(e){switch(e.keyCode||e.which){case 16:h.shift_down=!0;break;case 17:h.ctrl_down=!0}}key_up(e){switch(e.keyCode||e.which){case 16:h.shift_down=!1;break;case 17:h.ctrl_down=!1}}}class d{static initialize(e,t=null){const i=s.query_elem("input#search-text",e);i&&(null===t&&(t=cone.LiveSearch),new t(i))}constructor(e){this.elem=e,this.target=`${e.data("search-target")}/livesearch`,this.result=t("\n ${void 0===e.description?"":e.description}\n
\n\n ${e.length} Results\n
\n `,this.result);for(const t of e)this.render_suggestion(t)}else this.render_no_results();this.result.tsajax()}on_keydown(e){13!==e.keyCode&&s.clock.schedule_frame((()=>{this._term!==this.elem.val()&&this.elem.trigger("change")}))}on_change(e){if(this._in_progress)return;const t=this.elem.val();this._term!==t&&(this._term=t,this._term.length