Skip to content

Commit

Permalink
PageLink part to link pages (SpinaCMS#1193)
Browse files Browse the repository at this point in the history
* Page select

* Page select

* Search

* Support multiple pagelinks

* Margin added

* Update Tailwind

* Remove rescue calls

* Translate placeholder
  • Loading branch information
Bramjetten authored Feb 22, 2023
1 parent 9e92c5d commit 46b13ea
Show file tree
Hide file tree
Showing 15 changed files with 244 additions and 8 deletions.
4 changes: 4 additions & 0 deletions app/assets/builds/spina/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -2234,6 +2234,10 @@ trix-editor [data-trix-mutable]::selection,
height: 100%;
}

.max-h-80 {
max-height: 20rem;
}

.max-h-full {
max-height: 100%;
}
Expand Down
15 changes: 14 additions & 1 deletion app/assets/javascripts/spina/controllers/form_controller.js
Original file line number Diff line number Diff line change
@@ -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
}

}
38 changes: 38 additions & 0 deletions app/assets/javascripts/spina/controllers/page_select_controller.js
Original file line number Diff line number Diff line change
@@ -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 = `
<span class="text-gray-400">
${this.element.dataset.placeholder}
</span>
`
}

autofocus() {
setTimeout(function() {
this.searchTarget.focus()
}.bind(this), 100)
}

}
65 changes: 65 additions & 0 deletions app/assets/javascripts/spina/libraries/debounce.js
Original file line number Diff line number Diff line change
@@ -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;
};
24 changes: 24 additions & 0 deletions app/controllers/spina/admin/page_select_options_controller.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion app/controllers/spina/admin/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def destroy

redirect_to spina.admin_pages_url(resource_id: @page.resource_id)
end

private

def set_locale
Expand Down
13 changes: 13 additions & 0 deletions app/models/spina/parts/page_link.rb
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions app/views/spina/admin/page_select_options/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= turbo_frame_tag "page_select_options_#{params[:object_id]}" do %>
<% if params[:search].blank? %>
<button type="button" data-action="page-select#clear reveal#hide" class="block hover:bg-gray-100 w-full text-left px-5 py-2 font-medium leading-5 text-gray-700 hover:text-gray-900 text-sm border-t border-gray-150">
</button>
<% end %>

<% @pages.each do |page| %>
<button type="button" data-action="page-select#select reveal#hide" data-title="<%= page.title %>" data-id="<%= page.id %>" class="block hover:bg-gray-100 w-full text-left px-5 py-2 font-medium leading-5 text-gray-700 hover:text-gray-900 text-sm border-t border-gray-150">
<%= page.title %>
<% if page.draft? %>
<span class="font-normal text-gray-400">
(<%=t "spina.pages.concept" %>)
</span>
<% end %>
<div class="text-xs font-normal text-gray-400">
<%= page.materialized_path %>
</div>
</button>
<% end %>
<% end %>
3 changes: 3 additions & 0 deletions app/views/spina/admin/page_select_options/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= turbo_frame_tag :page_title do %>
<%= @page.title %>
<% end %>
36 changes: 36 additions & 0 deletions app/views/spina/admin/parts/page_links/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<div class="mt-6">
<label class="block text-sm leading-5 font-medium text-gray-700"><%= f.object.title %></label>
<div class="text-gray-400 text-sm"><%= f.object.hint %></div>

<div data-controller="page-select reveal" data-placeholder="<%=t "spina.pages.select_page" %>" class="relative mt-1" data-reveal-away-value>
<%= f.hidden_field :page_id, data: {page_select_target: "input"} %>

<button type="button" class="btn btn-default px-3 inline-flex items-center text-sm font-medium" data-action="reveal#toggle page-select#autofocus">
<%= heroicon("link", style: :mini, class: "w-4 h-4 mr-1 text-gray-600") %>
<div data-page-select-target="label">
<% if f.object.page_id.present? %>
<%= turbo_frame_tag :page_title, src: spina.admin_page_select_option_path(f.object.page_id) %>
<% end %>
</div>
</button>

<div class="relative mt-1">
<div data-reveal data-transition hidden class="absolute shadow-lg border border-gray-200 origin-top-right rounded-md z-10 top-0">
<div class="rounded-md bg-white shadow-xs">
<%= form_with url: spina.search_admin_page_select_options_path, data: {turbo_frame: "page_select_options_#{f.object.object_id}", controller: "form", debounce_time: 100} do |ff| %>
<%= ff.hidden_field :object_id, value: f.object.object_id %>
<%= ff.hidden_field :resource, value: f.object.options&.dig(:resource) %>
<div class="p-2">
<%= ff.search_field :search, placeholder: t("spina.search"), class: "form-input sticky top-0 text-sm w-80", data: {action: "input->form#submit focus->form#submit", page_select_target: "search"} %>
</div>
<% end %>

<div class="overflow-scroll max-h-80">
<%= turbo_frame_tag "page_select_options_#{f.object.object_id}" do %>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ en:
save_draft: Save draft
saved: Page saved
saving: Saving...
select_page: Select page
couldnt_be_saved: Page couldn't be saved
search_engines: Search engines
show_in_menu: invisible
Expand Down
3 changes: 2 additions & 1 deletion config/locales/nl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ nl:
change_view_template: Wijzig pagina template
change_view_template_confirmation: Weet je zeker dat je de pagina template wil veranderen? Inhoud die niet op de nieuwe pagina past gaat verloren.
concept: concept
couldnt_be_saved: Pagina kon niet worden opgeslagen
create: Maak pagina
create_page: "%{template} invullen"
delete: Verwijder pagina
Expand Down Expand Up @@ -277,7 +278,7 @@ nl:
save_draft: Concept opslaan
saved: Pagina opgeslagen
saving: "<i></i> Opslaan..."
couldnt_be_saved: Pagina kon niet worden opgeslagen
select_page: Kies pagina
search_engines: Zoekmachines
show_in_menu: onzichtbaar
skip_to_first_child: doorsturen naar 1<sup>e</sup> subpagina
Expand Down
11 changes: 8 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@
get :edit_content
get :edit_template
get :children
post :sort_one
end

collection do
post :sort
end

resource :move, controller: "move_pages"

post :sort, on: :collection
post :sort_one, on: :member
end
resources :page_select_options, only: [:show, :index] do
post :search, on: :collection
end
resources :page_translations, only: [:destroy]
resources :parent_pages
Expand Down
3 changes: 2 additions & 1 deletion lib/spina/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class Engine < ::Rails::Engine
Spina::Parts::ImageCollection,
Spina::Parts::Repeater,
Spina::Parts::Option,
Spina::Parts::Attachment
Spina::Parts::Attachment,
Spina::Parts::PageLink
)
end
end
Expand Down
13 changes: 12 additions & 1 deletion test/dummy/config/initializers/themes/demo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@
title: "Testrepeater",
part_type: "Spina::Parts::Repeater",
parts: %w[line body]
}, {
name: "page",
title: "Pagina",
part_type: "Spina::Parts::PageLink"
}, {
name: "blogpost",
title: "Blogpost",
part_type: "Spina::Parts::PageLink",
options: {
resource: "blog"
}
}]

theme.view_templates = [{
Expand All @@ -69,7 +80,7 @@
title: "Simple page",
description: "Default layout",
usage: "Use for your content",
parts: ["body", "testrepeater"]
parts: ["body", "blogpost", "page", "testrepeater"]
}, {
name: "demo",
title: "Demo",
Expand Down

0 comments on commit 46b13ea

Please sign in to comment.