diff --git a/app/assets/builds/spina/tailwind.css b/app/assets/builds/spina/tailwind.css index 2f3a4acdf..338a19661 100644 --- a/app/assets/builds/spina/tailwind.css +++ b/app/assets/builds/spina/tailwind.css @@ -2234,6 +2234,10 @@ trix-editor [data-trix-mutable]::selection, height: 100%; } +.max-h-80 { + max-height: 20rem; +} + .max-h-full { max-height: 100%; } diff --git a/app/assets/javascripts/spina/controllers/form_controller.js b/app/assets/javascripts/spina/controllers/form_controller.js index 8021a6b26..63967fcad 100644 --- a/app/assets/javascripts/spina/controllers/form_controller.js +++ b/app/assets/javascripts/spina/controllers/form_controller.js @@ -1,10 +1,23 @@ import { Controller } from "@hotwired/stimulus" +import debounce from "libraries/debounce" import formRequestSubmitPolyfill from "libraries/form-request-submit-polyfill" export default class extends Controller { - requestSubmit() { + submitForm = debounce(function() { this.element.requestSubmit() + }.bind(this), this.debounceTime) + + requestSubmit() { + this.submitForm() + } + + submit() { + this.submitForm() + } + + get debounceTime() { + return this.element.dataset.debounceTime || 0 } } \ No newline at end of file diff --git a/app/assets/javascripts/spina/controllers/page_select_controller.js b/app/assets/javascripts/spina/controllers/page_select_controller.js new file mode 100644 index 000000000..179f0b25c --- /dev/null +++ b/app/assets/javascripts/spina/controllers/page_select_controller.js @@ -0,0 +1,38 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + + static get targets() { + return ['input', 'label', 'search'] + } + + connect() { + // Show placeholder if there is no page selected yet + if (this.labelTarget.querySelector("turbo-frame") == undefined) { + this.clear() + } + } + + select(event) { + let button = event.currentTarget + + this.inputTarget.value = button.dataset.id + this.labelTarget.innerText = button.dataset.title + } + + clear() { + this.inputTarget.value = "" + this.labelTarget.innerHTML = ` + + ${this.element.dataset.placeholder} + + ` + } + + autofocus() { + setTimeout(function() { + this.searchTarget.focus() + }.bind(this), 100) + } + +} \ No newline at end of file diff --git a/app/assets/javascripts/spina/libraries/debounce.js b/app/assets/javascripts/spina/libraries/debounce.js new file mode 100644 index 000000000..f078d6056 --- /dev/null +++ b/app/assets/javascripts/spina/libraries/debounce.js @@ -0,0 +1,65 @@ +/** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. If `immediate` is passed, trigger the function on the + * leading edge, instead of the trailing. The function also has a property 'clear' + * that is a function which will clear the timer to prevent previously scheduled executions. + * + * @source underscore.js + * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * @param {Function} function to wrap + * @param {Number} timeout in ms (`100`) + * @param {Boolean} whether to execute at the beginning (`false`) + * @api public + */ +export default function debounce(func, wait, immediate){ + var timeout, args, context, timestamp, result; + if (null == wait) wait = 100; + + function later() { + var last = Date.now() - timestamp; + + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + var debounced = function(){ + context = this; + args = arguments; + timestamp = Date.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + + debounced.clear = function() { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + }; + + debounced.flush = function() { + if (timeout) { + result = func.apply(context, args); + context = args = null; + + clearTimeout(timeout); + timeout = null; + } + }; + + return debounced; +}; diff --git a/app/controllers/spina/admin/page_select_options_controller.rb b/app/controllers/spina/admin/page_select_options_controller.rb new file mode 100644 index 000000000..f63e1d65f --- /dev/null +++ b/app/controllers/spina/admin/page_select_options_controller.rb @@ -0,0 +1,24 @@ +module Spina + module Admin + class PageSelectOptionsController < AdminController + + def show + @page = Page.find(params[:id]) + end + + def index + end + + def search + if params[:resource].present? + @pages = Resource.find_by(name: params[:resource])&.pages + end + + @pages ||= Page.all + @pages = @pages.joins(:translations).where("spina_page_translations.title ILIKE :query OR materialized_path ILIKE :query", query: "%#{params[:search]}%").order(created_at: :desc).distinct.page(params[:page]).per(20) + render :index + end + + end + end +end \ No newline at end of file diff --git a/app/controllers/spina/admin/pages_controller.rb b/app/controllers/spina/admin/pages_controller.rb index bb1e643d8..f6d0df60f 100644 --- a/app/controllers/spina/admin/pages_controller.rb +++ b/app/controllers/spina/admin/pages_controller.rb @@ -108,7 +108,7 @@ def destroy redirect_to spina.admin_pages_url(resource_id: @page.resource_id) end - + private def set_locale diff --git a/app/models/spina/parts/page_link.rb b/app/models/spina/parts/page_link.rb new file mode 100644 index 000000000..e5a204dac --- /dev/null +++ b/app/models/spina/parts/page_link.rb @@ -0,0 +1,13 @@ +module Spina + module Parts + class PageLink < Base + attr_json :page_id, :integer, default: nil + + attr_accessor :options + + def content + Page.live.find_by(id: page_id) + end + end + end +end diff --git a/app/views/spina/admin/page_select_options/index.html.erb b/app/views/spina/admin/page_select_options/index.html.erb new file mode 100644 index 000000000..802b011fb --- /dev/null +++ b/app/views/spina/admin/page_select_options/index.html.erb @@ -0,0 +1,21 @@ +<%= turbo_frame_tag "page_select_options_#{params[:object_id]}" do %> + <% if params[:search].blank? %> + + <% end %> + + <% @pages.each do |page| %> + + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/spina/admin/page_select_options/show.html.erb b/app/views/spina/admin/page_select_options/show.html.erb new file mode 100644 index 000000000..81e938cf6 --- /dev/null +++ b/app/views/spina/admin/page_select_options/show.html.erb @@ -0,0 +1,3 @@ +<%= turbo_frame_tag :page_title do %> + <%= @page.title %> +<% end %> \ No newline at end of file diff --git a/app/views/spina/admin/parts/page_links/_form.html.erb b/app/views/spina/admin/parts/page_links/_form.html.erb new file mode 100644 index 000000000..b5c8633f7 --- /dev/null +++ b/app/views/spina/admin/parts/page_links/_form.html.erb @@ -0,0 +1,36 @@ +