mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
514 lines
15 KiB
514 lines
15 KiB
# frozen_string_literal: true
|
|
|
|
# DESCRIPTION
|
|
# Showcase all the helpers and styles
|
|
#
|
|
# DOC
|
|
# https://ddnexus.github.io/pagy/playground/#demo-app
|
|
#
|
|
# BIN HELP
|
|
# pagy -h
|
|
#
|
|
# DEMO USAGE
|
|
# pagy demo
|
|
#
|
|
# DEV USAGE
|
|
# pagy clone demo
|
|
# pagy ./demo.ru
|
|
#
|
|
# URL
|
|
# http://127.0.0.1:8000
|
|
|
|
VERSION = '43.4.4'
|
|
|
|
if VERSION != Pagy::VERSION
|
|
Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
|
|
end
|
|
|
|
# Bundle
|
|
require 'bundler/inline'
|
|
gemfile(!Pagy::ROOT.join('pagy.gemspec').exist?) do
|
|
source 'https://rubygems.org'
|
|
gem 'oj'
|
|
gem 'puma'
|
|
gem 'rouge'
|
|
gem 'sinatra'
|
|
end
|
|
|
|
# pagy initializer
|
|
SECTIONS = { pagy: { css_anchor: 'pagy-css' },
|
|
tailwind: { css_anchor: 'pagy-tailwind-css' },
|
|
bootstrap: { style: :bootstrap, classes: 'pagination pagination-sm' },
|
|
bulma: { style: :bulma, classes: 'pagination is-small' },
|
|
template: { css_anchor: 'pagy-css', template: :template } }.freeze
|
|
|
|
# Sinatra setup
|
|
require 'sinatra/base'
|
|
|
|
# Pagy init
|
|
Pagy::OPTIONS[:client_max_limit] = 100
|
|
|
|
# Sinatra application
|
|
class PagyDemo < Sinatra::Base
|
|
include Pagy::Method
|
|
|
|
get '/' do
|
|
redirect '/pagy'
|
|
end
|
|
|
|
get('/javascripts/:file') do
|
|
format = params[:file].split('.').last
|
|
if format == 'js'
|
|
content_type 'application/javascript'
|
|
elsif format == 'map'
|
|
content_type 'application/json'
|
|
end
|
|
send_file Pagy::ROOT.join('javascripts', params[:file])
|
|
end
|
|
|
|
get('/stylesheets/:file') do
|
|
content_type 'text/css'
|
|
send_file Pagy::ROOT.join('stylesheets', params[:file])
|
|
end
|
|
|
|
if ENV['E2E_TEST']
|
|
get('/assets/:file') do
|
|
content_type 'text/css'
|
|
send_file Pagy::ROOT.join('../test/e2e/assets', params[:file])
|
|
end
|
|
end
|
|
|
|
# One route/action per style
|
|
SECTIONS.each do |section, value|
|
|
get("/#{section}") do
|
|
collection = MockCollection.new
|
|
@pagy, @records = pagy(:offset, collection)
|
|
erb value[:template] || :page,
|
|
locals: { section:,
|
|
pagy: @pagy,
|
|
style: value[:style],
|
|
classes: value[:classes],
|
|
css_anchor: value[:css_anchor] }
|
|
end
|
|
end
|
|
|
|
PAGY_LIKE_HEAD =
|
|
%(#{Pagy.dev_tools unless ENV['E2E_TEST']}
|
|
<style>
|
|
/* black/white backdrop color based on --B */
|
|
.pagy { background-color: hsl(0 0 calc(100 * var(--B))) !important; }
|
|
</style>).freeze
|
|
|
|
helpers do
|
|
def style_menu
|
|
html = +%(<div id="style-menu"> )
|
|
SECTIONS.each_key do |section|
|
|
name = section.to_s
|
|
name[0] = name[0].capitalize
|
|
html << %(<a href="/#{section}">#{name}</a>)
|
|
end
|
|
html << %(</div>)
|
|
html
|
|
end
|
|
|
|
def head_for(section)
|
|
case section
|
|
when :pagy, :template
|
|
%(#{PAGY_LIKE_HEAD}
|
|
<link rel="stylesheet" href="/stylesheets/pagy.css">)
|
|
when :tailwind
|
|
%(#{PAGY_LIKE_HEAD}
|
|
#{if ENV['E2E_TEST']
|
|
'<script src="/assets/tailwind.js"></script>'
|
|
else
|
|
'<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>'
|
|
end}
|
|
<style type="text/tailwindcss">
|
|
#{Pagy::ROOT.join('stylesheets/pagy-tailwind.css').read}
|
|
</style>)
|
|
when :bootstrap
|
|
if ENV['E2E_TEST']
|
|
'<link rel="stylesheet" href="/assets/bootstrap.min.css">'
|
|
else
|
|
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">'
|
|
end
|
|
when :bulma
|
|
if ENV['E2E_TEST']
|
|
'<link rel="stylesheet" href="/assets/bulma.min.css">'
|
|
else
|
|
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1/css/bulma.min.css">'
|
|
end
|
|
end
|
|
end
|
|
|
|
def highlight(html, format: :html)
|
|
if format == :html
|
|
html = html.gsub(/>\s*</, '><').strip # template single line no spaces around/between tags
|
|
html = Formatter.new.format(html)
|
|
end
|
|
lexer = Rouge::Lexers::ERB.new
|
|
formatter = Rouge::Formatters::HTMLInline.new('monokai.sublime')
|
|
summary = { html: 'Served HTML (pretty formatted)', erb: 'ERB Template' }
|
|
%(<details><summary>#{summary[format]}</summary><pre>\n#{
|
|
formatter.format(lexer.lex(html))
|
|
}</pre></details>)
|
|
end
|
|
end
|
|
|
|
# Views
|
|
template :layout do
|
|
<<~HTML
|
|
<!DOCTYPE html>
|
|
<html lang="en" data-theme="light">
|
|
<head>
|
|
<title>Pagy Demo App</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<%= head_for(section) %>
|
|
<script src="/javascripts/pagy.js"></script>
|
|
<script>window.addEventListener("load", Pagy.init);</script>
|
|
<% unless ENV['E2E_TEST'] %>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap" rel="stylesheet">
|
|
<% end %>
|
|
<style>
|
|
@media screen { html, body {
|
|
font-size: 1rem;
|
|
line-height: 1.2;
|
|
padding: 0;
|
|
margin: 0;
|
|
} }
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
box-sizing: border-box;
|
|
}
|
|
html {
|
|
background-color: transparent !important; /* Fix for Bulma */
|
|
}
|
|
body {
|
|
margin: 0 !important;
|
|
font-family: <%= '"Nunito Sans", ' unless ENV['E2E_TEST'] %>"Helvetica Neue", Helvetica, Arial, sans-serif !important;
|
|
color: #303030 !important;
|
|
background-color: #f5f5f5 !important;
|
|
}
|
|
svg {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
z-index: -10;
|
|
}
|
|
h1, h2 {
|
|
font-size: 1.8rem !important;
|
|
font-weight: 600 !important;
|
|
margin-top: 1rem !important;
|
|
margin-bottom: 0.7rem !important;
|
|
line-height: 1.5 !important;
|
|
color: #303030 !important;
|
|
}
|
|
h2 {
|
|
font-family: monospace;
|
|
font-size: .9rem !important;
|
|
margin-top: 1.6rem !important;
|
|
}
|
|
hr {
|
|
height: 0;
|
|
color: inherit;
|
|
border-top-width: 1px;
|
|
border-top-color: gray;
|
|
margin: 8px 0 !important;
|
|
}
|
|
summary, .notes {
|
|
font-size: .8rem;
|
|
margin-top: .6rem;
|
|
font-style: italic;
|
|
cursor: pointer;
|
|
}
|
|
.notes {
|
|
font-family: sans-serif;
|
|
font-weight: normal;
|
|
}
|
|
.notes code{
|
|
background-color: rgba(255, 255, 255, .6);
|
|
padding: 0 0.3rem;
|
|
font-style: normal;
|
|
border-radius: 3px;
|
|
}
|
|
.description {
|
|
margin: 1rem 0;
|
|
}
|
|
.description a {
|
|
color: blue;
|
|
text-decoration: underline;
|
|
}
|
|
pre, pre code {
|
|
display: inline-block;
|
|
margin-top: .3rem;
|
|
margin-bottom: 1rem;
|
|
font-size: .8rem !important;
|
|
line-height: 1rem !important;
|
|
color: white;
|
|
background-color: rgb(30 30 30);
|
|
padding: 1rem;
|
|
overflow-x: auto;
|
|
max-width: 100%;
|
|
white-space: pre;
|
|
}
|
|
.main-content {
|
|
padding: 0 1.5rem 2rem !important;
|
|
}
|
|
#style-menu {
|
|
flex;
|
|
font-family: sans-serif;
|
|
font-size: 1.1rem;
|
|
line-height: 1.5rem;
|
|
white-space: nowrap;
|
|
color: white;
|
|
background-color: rgba(0,0,0,.65);
|
|
padding: .2rem 1.5rem;
|
|
}
|
|
#style-menu > :not([hidden]) ~ :not([hidden]) {
|
|
--space-reverse: 0;
|
|
margin-right: calc(0.5rem * var(--space-reverse));
|
|
margin-left: calc(0.5rem * calc(1 - var(--space-reverse)));
|
|
}
|
|
#style-menu a {
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
.pagy, .pagy-bootstrap, .pagy-bulma {
|
|
background-color: white;
|
|
padding: 1.5em;
|
|
margin: .3em 0;
|
|
width: fit-content;
|
|
box-shadow: 8px 8px 18px 0px rgba(0,0,0,0.25);
|
|
}
|
|
span.pagy {
|
|
display: block;
|
|
}
|
|
.pagy-bootstrap .pagination {
|
|
margin: 0;
|
|
}
|
|
/* Demo app custom style */
|
|
.pagy {
|
|
--B: 1;
|
|
--H: 109;
|
|
--S: 40;
|
|
--L: 70;
|
|
--spacing: 0.125rem;
|
|
--padding: 0.75rem;
|
|
--rounding: 0.8125rem;
|
|
--border-width: 0.03125rem;
|
|
--font-size: 0.875rem;
|
|
--font-weight: 450;
|
|
--line-height: 1.75;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
|
|
<filter id="noiseFilter">
|
|
<feTurbulence type="fractalNoise" baseFrequency="0.6" numOctaves="100" stitchTiles="stitch" />
|
|
<feColorMatrix type="matrix" values="0.5 0 0 0 0, 0.5 0 0 0 0, 0.5 0 0 0 0, 0 0 0 0.5 0" />
|
|
</filter>
|
|
<rect width="100%" height="100%" filter="url(#noiseFilter)" fill="rgb(255, 255, 255)" />
|
|
</svg>
|
|
<!-- each different class used by each style -->
|
|
<%= style_menu %>
|
|
<div class="main-content">
|
|
<%= yield %>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
HTML
|
|
end
|
|
|
|
template :page do
|
|
<<~ERB
|
|
<h1><%= title = section.to_s; title[0] = title[0].capitalize; title %></h1>
|
|
|
|
<% if css_anchor %>
|
|
<p>Check the <u><i><b><a href="http://ddnexus.github.io/pagy/resources/stylesheets/#<%= css_anchor %>" target="blank"><%= css_anchor.gsub('-', '.') %></a></b></u></i>
|
|
for details.</p>
|
|
<% end %>
|
|
</p>
|
|
<div id="main-container">
|
|
<div id="content">
|
|
<hr id="top-hr">
|
|
<h2>@records</h2>
|
|
<p id="records"><%= @records.join(',') %></p>
|
|
|
|
<h2>@pagy.series_nav<br/>
|
|
<span class="notes">Series nav <code>{slots: 7}</code></span>
|
|
</h2>
|
|
<%= html = @pagy.series_nav(style, classes:,
|
|
id: 'series-nav',
|
|
aria_label: 'Pages series_nav') %>
|
|
<%= highlight(html) %>
|
|
|
|
<h2>@pagy.series_nav_js<br/>
|
|
<span class="notes">Responsive nav: <code>{steps: {0 => 5, 500 => 7, 600 => 9, 700 => 11}}</code><br/>
|
|
(Resize the window to see)
|
|
</span>
|
|
</h2>
|
|
<%= html = @pagy.series_nav_js(style, classes:,
|
|
id: 'series-nav-js-responsive',
|
|
aria_label: 'Pages series_nav_js_responsive',
|
|
steps: { 0 => 5, 500 => 7, 600 => 9, 700 => 11 }) %>
|
|
<%= highlight(html) %>
|
|
|
|
<h2>@pagy.input_nav_js</h2>
|
|
<%= html = @pagy.input_nav_js(style, classes:,
|
|
id: 'input-nav-js',
|
|
aria_label: 'Pages inpup_nav_js') %>
|
|
<%= highlight(html) %>
|
|
|
|
<h2>@pagy.limit_tag_js</h2>
|
|
<%= html = @pagy.limit_tag_js(id: 'limit-tag-js') %>
|
|
<%= highlight(html) %>
|
|
|
|
<h2>@pagy.info_tag</h2>
|
|
<%= html = @pagy.info_tag(id: 'pagy-info') %>
|
|
<%= highlight(html) %>
|
|
</div>
|
|
</div>
|
|
ERB
|
|
end
|
|
|
|
template :template do
|
|
<<~ERB
|
|
<h1>Pagy Template Demo</h1>
|
|
|
|
<p class="description">
|
|
See the <a href="https://ddnexus.github.io/pagy/docs/how-to/#using-your-pagination-templates">
|
|
Custom Templates</a> documentation.
|
|
</p>
|
|
|
|
<h2>Collection</h2>
|
|
<p id="records">@records: <%= @records.join(',') %></p>
|
|
|
|
<h2>Rendered ERB template</h2>
|
|
|
|
<div class="backdrop">
|
|
<%# We don't inline the template here, so we can highlight it more easily %>
|
|
<%= html = ERB.new(TEMPLATE).result(binding) %>
|
|
</div>
|
|
<%= highlight(TEMPLATE, format: :erb) %>
|
|
<%= highlight(html) %>
|
|
ERB
|
|
end
|
|
|
|
# Easier code highlighting
|
|
TEMPLATE = <<~ERB
|
|
<%# IMPORTANT: replace '<%=' with '<%==' if you run this in rails %>
|
|
|
|
<%# The a variable below is set to a lambda that generates the a tag %>
|
|
<%# Usage: anchor_tag = a_lambda.(page_number, text, classes: nil, aria_label: nil) %>
|
|
<% a_lambda = @pagy.send(:a_lambda) %>
|
|
<nav class="pagy series-nav" aria-label="Pages">
|
|
<%# Previous page link %>
|
|
<% if pagy.previous %>
|
|
<%= a_lambda.(pagy.previous, '<', aria_label: 'Previous') %>
|
|
<% else %>
|
|
<a role="link" aria-disabled="true" aria-label="Previous"><</a>
|
|
<% end %>
|
|
<%# Page links (series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]) %>
|
|
<% pagy.send(:series).each do |item| %>
|
|
<% if item.is_a?(Integer) %>
|
|
<%= a_lambda.(item) %>
|
|
<% elsif item.is_a?(String) %>
|
|
<a role="link" aria-disabled="true" aria-current="page"><%= item %></a>
|
|
<% elsif item == :gap %>
|
|
<a role="separator" aria-disabled="true">…</a>
|
|
<% end %>
|
|
<% end %>
|
|
<%# Next page link %>
|
|
<% if pagy.next %>
|
|
<%= a_lambda.(pagy.next, '>', aria_label: 'Next') %>
|
|
<% else %>
|
|
<a role="link" aria-disabled="true" aria-label="Next"><</a>
|
|
<% end %>
|
|
</nav>
|
|
ERB
|
|
end
|
|
|
|
# Cheap pagy formatter for helpers output
|
|
class Formatter
|
|
INDENT = ' '
|
|
TEXT_SPACE = "\u00B7"
|
|
TEXT = /^([^<>]+)(.*)/
|
|
UNPAIRED = /^(<(input|link).*?>)(.*)/
|
|
PAIRED = %r{^(<(head|nav|div|span|p|a|b|label|ul|li).*?>)(.*?)(</\2>)(.*)}
|
|
WRAPPER = /<.*?>/
|
|
DATA_PAGY = /(data-pagy="([^"]+)")/
|
|
|
|
def initialize
|
|
@formatted = +''
|
|
end
|
|
|
|
def format(input, level = 0)
|
|
process(input, level)
|
|
@formatted
|
|
end
|
|
|
|
private
|
|
|
|
def process(input, level)
|
|
push = ->(content) { @formatted << ((INDENT * level) << content << "\n") }
|
|
rest = nil
|
|
if (match = input.match(TEXT))
|
|
text, rest = match.captures
|
|
push.(text.gsub(' ', TEXT_SPACE))
|
|
elsif (match = input.match(UNPAIRED))
|
|
tag, _name, rest = match.captures
|
|
push.(tag)
|
|
elsif (match = input.match(PAIRED))
|
|
tag_start, name, block, tag_end, rest = match.captures
|
|
# Handle incomplete same-tag nesting
|
|
while block.scan(/<#{name}.*?>/).size > block.scan(tag_end).size
|
|
more, rest = rest.split(tag_end, 2)
|
|
block << (tag_end + more)
|
|
end
|
|
if (match = tag_start.match(DATA_PAGY))
|
|
search, data = match.captures
|
|
formatted = data.scan(/.{1,76}/).join("\n")
|
|
replace = %(\n#{INDENT * (level + 1)}data-pagy="#{formatted}")
|
|
tag_start.sub!(search, replace)
|
|
end
|
|
if block.match(WRAPPER)
|
|
push.(tag_start)
|
|
process(block, level + 1)
|
|
push.(tag_end)
|
|
else
|
|
push.(tag_start << (block + tag_end))
|
|
end
|
|
end
|
|
process(rest, level) if rest
|
|
end
|
|
end
|
|
|
|
# Simple array-based collection that acts as a standard DB collection.
|
|
class MockCollection < Array
|
|
def initialize(arr = Array(1..1000))
|
|
super
|
|
@collection = clone
|
|
end
|
|
|
|
def offset(value)
|
|
@collection = self[value..] || []
|
|
self
|
|
end
|
|
|
|
def limit(value)
|
|
@collection.empty? ? [] : @collection[0, value]
|
|
end
|
|
|
|
def count(*)
|
|
size
|
|
end
|
|
end
|
|
|
|
run PagyDemo
|