Skip to content

Commit

Permalink
[jarun#753] migrate to Bootstrap v4
Browse files Browse the repository at this point in the history
  • Loading branch information
LeXofLeviafan committed Nov 8, 2024
1 parent bfe195e commit dd05421
Show file tree
Hide file tree
Showing 20 changed files with 127 additions and 85 deletions.
2 changes: 1 addition & 1 deletion bukuserver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ The following are os env config variables available for bukuserver.
| DISABLE_FAVICON | disable bookmark [favicons](https://wikipedia.org/wiki/Favicon) | boolean [default: `true`] ([here's why](#why-favicons-are-disabled-by-default))|
| OPEN_IN_NEW_TAB | url link open in new tab | boolean [default: `false`] |
| REVERSE_PROXY_PATH | reverse proxy path | string |
| THEME | [GUI theme](https://bootswatch.com/3) | string [default: `default`] (`slate` is a good pick for dark mode) |
| THEME | [GUI theme](https://bootswatch.com/4) | string [default: `default`] (`slate` is a good pick for dark mode) |
| LOCALE | GUI language (partial support) | string [default: `en`] |
| DEBUG | debug mode (verbose logging etc.) | boolean [default: `false`] |

Expand Down
2 changes: 1 addition & 1 deletion bukuserver/bookmarklet.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ url = "{{url}}" +
"?url=" + encodeURIComponent(url) +
"&title=" + encodeURIComponent(title) +
"&description=" + encodeURIComponent(desc);
window.open(url, '_blank', 'menubar=no, height=600, width=800, toolbar=no, scrollbars=yes, status=no, dialog=1');
window.open(url, '_blank', 'menubar=no, height=700, width=800, toolbar=no, scrollbars=yes, status=no, dialog=1');
2 changes: 1 addition & 1 deletion bukuserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def shell_context():
app.jinja_env.globals.update(_p=_p)

admin = Admin(
app, name='buku server', template_mode='bootstrap3',
app, name='buku server', template_mode='bootstrap4',
index_view=views.CustomAdminIndexView(
template='bukuserver/home.html', url='/'
)
Expand Down
3 changes: 3 additions & 0 deletions bukuserver/static/bukuserver/css/bookmark.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ table tr td:not(:first-child) {
}
}

/* fixing details filter width */
#fa_filter {flex-grow: 1}

.link {
display: block;
}
Expand Down
29 changes: 21 additions & 8 deletions bukuserver/static/bukuserver/css/list.css
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
/* overriding icon-button text color with theme color */
form.icon button {
color: inherit;
}

/* fixing table layout */
.list-buttons-column {width: 0}
.list-buttons-column .icon:last-child {margin-right: 10px}

.filters .filter-op {width: var(--filter-op) !important}
.filters .filter-val {width: calc(var(--filters) - var(--filter-op) - var(--filter-buttons) - var(--filter-type)) !important}
#filter_form[action^='/tag/'] {--filter-type: var(--filter-type-tags)}

:root {--filters: 645px; --filter-op: 9em; --filter-buttons: 12.5em; --filter-type: 5.5em; --filter-type-tags: 9em}
:root {--filters: 510px; --filter-op: 9rem; --filter-buttons: 12.5rem; --filter-type: 6rem; --filter-type-tags: 9.5rem}
/* due to how flask-admin filters are set up, each language requires manual adjustments for full-width sizes */
html[lang=de] {--filter-buttons: 19em}
html[lang=fr] {--filter-buttons: 17em}
html[lang=ru] {--filter-buttons: 16.5em; --filter-type: 8.5em; --filter-type-tags: 11em}
html[lang=de] {--filter-buttons: 18rem}
html[lang=fr] {--filter-buttons: 16rem}
html[lang=ru] {--filter-buttons: 16.5rem; --filter-type: 8.5rem; --filter-type-tags: 11.5rem}

@media (max-width: 767px) {
.filters .filter-val {width: calc(var(--filters) - var(--filter-op) - var(--filter-type)) !important}
#filter_form .pull-right:first-child {margin: .5ex 0}
}
@media (min-width: 768px) {
:root {--filters: 710px; --filter-op: 11.5em}
:root {--filters: 690px; --filter-op: 11.5rem}
}
@media (min-width: 992px) {
:root {--filters: 930px; --filter-op: 20em}
:root {--filters: 930px; --filter-op: 20rem}
}
@media (min-width: 1200px) {
:root {--filters: 1130px; --filter-op: 20em}
html[lang=ru] #filter_form[action^='/bookmark/'] {--filter-op: 25em} /* the last 'buku' filter has a rather long name */
:root {--filters: 1110px; --filter-op: 20rem}
html[lang=ru] #filter_form[action^='/bookmark/'] {--filter-op: 25rem} /* the last 'buku' filter has a rather long name */
}
2 changes: 1 addition & 1 deletion bukuserver/static/bukuserver/css/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ body.modal-open {

/* limit dialog height with a scrollbox */
.modal-content {
max-height: calc(100vh - 60px);
max-height: calc(100vh - 3.5rem);
display: flex;
flex-direction: column;
}
Expand Down
1 change: 1 addition & 0 deletions bukuserver/templates/bukuserver/bookmark_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
{{ buku.limit_navigation_if_popup() }}
{{ buku.script('bookmark.js') }}
{{ buku.fetch_checkbox(form.fetch.data) }}
{{ buku.horizontal_form(excluding_popups=True) }}
{{ buku.focus() }}
{{ buku.link_saved() }}
{% endblock %}
3 changes: 2 additions & 1 deletion bukuserver/templates/bukuserver/bookmark_create_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
{% block tail %}
{{ super() }}
{{ buku.script('bookmark.js') }}
{{ buku.fetch_checkbox(form.fetch.data) }}
{{ buku.fetch_checkbox(form.fetch.data, modal=True) }}
{{ buku.horizontal_form() }}
{{ buku.focus('.modal-body') }}
{% endblock %}
4 changes: 2 additions & 2 deletions bukuserver/templates/bukuserver/bookmark_details_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
{% block header_text %}
<h3>
{% if request.args.get('id') == 'random' %}
<button id="modal-random" class="btn btn-default" title="{{ _('Pick another') }}">
<span class="fa fa-eye glyphicon glyphicon-repeat"></span>
<button id="modal-random" class="btn btn-secondary" title="{{ _('Pick another') }}">
<span class="fa fa-repeat glyphicon glyphicon-repeat"></span>
</button>
{% endif %}
<a href="{{ url_for('bookmark.details_view', id=model.id, url=request.args.url) }}">{{_gettext('View Record')}} #{{model.id}}</a>
Expand Down
5 changes: 3 additions & 2 deletions bukuserver/templates/bukuserver/bookmark_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

{% block edit_form %}
{{ super() }}
<form method="POST" action="{{ get_url('.delete_view') }}" class="delete-form" style="display: inline-block; float: right">
<form method="POST" action="{{ get_url('.delete_view') }}" class="delete-form d-inline-block float-right">
<input type="hidden" name="id" value="{{ request.args.get('id') }}"/>
<input type="hidden" name="url" value="{{ return_url }}"/>
{% if csrf_token %}
Expand All @@ -24,7 +24,8 @@
{{ buku.set_lang() }}
{{ buku.limit_navigation_if_popup() }}
{{ buku.script('bookmark.js') }}
<script>$('.submit-row').append($('.delete-form'))</script>
{{ buku.horizontal_form(excluding_popups=True) }}
{{ buku.focus() }}
{{ buku.link_saved() }}
<script>$('.submit-row').append($('.delete-form'))</script>
{% endblock %}
1 change: 1 addition & 0 deletions bukuserver/templates/bukuserver/bookmark_edit_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
{% block tail %}
{{ super() }}
{{ buku.script('bookmark.js') }}
{{ buku.horizontal_form() }}
{{ buku.focus('.modal-body') }}
{% endblock %}
2 changes: 1 addition & 1 deletion bukuserver/templates/bukuserver/bookmarklet.url
Original file line number Diff line number Diff line change
@@ -1 +1 @@
javascript:void%20function(){var%20e=location.href,t=document.title.trim()||%22%22,o=document.getSelection().toString().trim()||(document.querySelector(%22meta[name$=description%20i],%20meta[property$=description%20i]%22)||{}).content||%22%22;o.length%3E4e3%26%26(o=o.substr(0,4e3)+%22...%22,alert(%22The%20selected%20text%20is%20too%20long,%20it%20will%20be%20truncated.%22)),e=%22{{url}}%3Furl=%22+encodeURIComponent(e)+%22%26title=%22+encodeURIComponent(t)+%22%26description=%22+encodeURIComponent(o),window.open(e,%22_blank%22,%22menubar=no,%20height=600,%20width=800,%20toolbar=no,%20scrollbars=yes,%20status=no,%20dialog=1%22)}();
javascript:void%20function(){var%20e=location.href,t=document.title.trim()||%22%22,o=document.getSelection().toString().trim()||(document.querySelector(%22meta[name$=description%20i],%20meta[property$=description%20i]%22)||{}).content||%22%22;o.length%3E4e3%26%26(o=o.substr(0,4e3)+%22...%22,alert(%22The%20selected%20text%20is%20too%20long,%20it%20will%20be%20truncated.%22)),e=%22{{url}}%3Furl=%22+encodeURIComponent(e)+%22%26title=%22+encodeURIComponent(t)+%22%26description=%22+encodeURIComponent(o),window.open(e,%22_blank%22,%22menubar=no,%20height=700,%20width=800,%20toolbar=no,%20scrollbars=yes,%20status=no,%20dialog=1%22)}();
10 changes: 6 additions & 4 deletions bukuserver/templates/bukuserver/bookmarks_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
{{ super() }}
{% if data %}
{% set _random = url_for('.details_view', modal=True, id='random', url=return_url, **(request.args|flt)) %}
<li><a id="random" data-target="#fa_modal_window" data-toggle="modal" href="{{ _random }}">{{ _('Random') }}</a></li>
<li class="nav-item">
<a id="random" class="nav-link" data-target="#fa_modal_window" data-toggle="modal" href="{{ _random }}">{{ _('Random') }}</a>
</li>
{% endif %}
{% endblock %}

Expand All @@ -46,13 +48,13 @@
{{ action.render_ctx(get_pk_value(row), row) }}
{% endfor %}
{% if request.args|flt|length == 0 %} {# only shown when filters/ordering are disabled #}
<div class="swap-toolbar" style="margin-left: 10px">
<div class="swap-toolbar" style="margin-left: 9px">
{% if row.id < 2 %}
<div style="display: inline-block; width: 14px"><!-- placeholder for the 1st row button --></div>
<div class="d-inline-block" style="width: 14px"><!-- placeholder for the 1st row button --></div>
{% else %}
{{ swap_rows_action('arrow-up', row.id, -1) }}
{% endif %}
{{ swap_rows_action('transfer', row.id) }}
{{ swap_rows_action('exchange', row.id) }}
{% if row.id < count %}
{{ swap_rows_action('arrow-down', row.id, +1) }}
{% endif %}
Expand Down
30 changes: 14 additions & 16 deletions bukuserver/templates/bukuserver/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,50 @@

{% block menu_links %}
{{ super() }}
<form class="navbar-form navbar-right" action="{{ url_for('admin.search') }}" method="POST">
<div class="form-group">
<input class="form-control" id="inputKeywords" placeholder="{{ _('Search bookmark') }}" name="keyword">
<form class="form-inline navbar-right" style="gap:.5ex" action="{{ url_for('admin.search') }}" method="POST">
<div class="d-inline-block align-middle">
<input class="form-control" id="inputKeywords" placeholder="{{ _('Search bookmark') }}" name="keyword"/>
<input type="hidden" name="markers" value="true"/>
<input type="hidden" name="all_keywords" value="true"/>
</div>
<button type="submit" class="btn btn-default">{{ _gettext('Search') }}</button>
<button type="submit" class="btn btn-secondary">{{ _gettext('Search') }}</button>
</form>
{% endblock %}

{% block body %}
{{ super() }}
<main class="container text-center">
<div style="padding: 40px 15px">
<main class="container text-center p-4">
<h1>BUKU</h1>
<p class="lead">{{ _('Bookmark manager like a text-based mini-web') }}</p>
<p>
<a class="btn btn-lg btn-success" href="{{ url_for('bookmark.index_view') }}" role="button">{{ _('Bookmarks') }}</a>
<a class="btn btn-lg btn-success" href="{{ url_for('tag.index_view') }}" role="button">{{ _('Tags') }}</a>
<a class="btn btn-lg btn-success" href="{{ url_for('statistic.index') }}" role="button">{{ _('Statistic') }}</a>
</p>
<div class="col-md-4 col-md-offset-4">
<div class="col-md-4 offset-md-4">
<form action="{{ url_for('admin.search') }}" method="POST">
<div class="form-group">
{{ form.keyword.label }}
{{ form.keyword(class_='form-control', style='display: inline; width: auto') }}
{{ form.keyword(class_='form-control d-inline', style='width: auto') }}
</div>
<div class="text-left col-sm-offset-3">
<div class="text-left">
{% for field in [form.all_keywords, form.markers, form.deep, form.regex] -%}
<div class="checkbox" title="{{ field.description }}" data-toggle="tooltip" data-placement="bottom"> {{field()}} {{field.label}} </div>
<div class="form-check" title="{{ field.description }}" data-toggle="tooltip" data-placement="bottom"> {{field()}} {{field.label}} </div>
{%- endfor %}
</div>
<button type="submit" class="btn btn-default">{{ _gettext('Search') }}</button>
<button type="submit" class="btn btn-secondary">{{ _gettext('Search') }}</button>
</form>
</div>
<div class="col-md-4 col-md-offset-4">
<p style="padding-top: 2em"> {{_('Bookmarklet')}}:
<div class="col-md-4 offset-md-4">
<p class="pt-4"> {{_('Bookmarklet')}}:
<a title="Drag this link to your bookmarks toolbar" href="{{ buku.bookmarklet() }}">
<b>✚ {{ _('Add to Buku') }}</b>
</a><br/>
<em style="font-size: smaller">{{ _("Note: if you select text on the page before activating the bookmarklet, it'll be used as description instead of page metadata.") }}</em>
</p>
</div>

<details class="col-md-6 col-md-offset-3">
<details class="col-md-6 offset-md-3">
<summary style="display: list-item; cursor: pointer"> <em><strong>{{ _('Location Bar (keyboard-only) shortcut') }}</strong></em> </summary>
<dl>
<dt>{{ _('in Firefox:') }}</dt>
Expand All @@ -76,15 +75,14 @@ <h1>BUKU</h1>
</dd>
</dl>
</details>
</div>
</main>
{% endblock %}

{% block tail %}
{{ buku.set_lang() }}
<script>
$(`[data-toggle="tooltip"]`).attr('data-html', 'true').each(function () {
this.title = this.title.replace(/'(.*?)'/g, `'<strong><tt>$1</tt></strong>'`)
this.title = this.title.replace(/'(.*?)'/g, `'<strong><code>$1</code></strong>'`)
.replace(/(?<=^|[^\p{L}]){{ _('FULL') }}(?=$|[^\p{L}])/g, `<strong><em>{{ _('FULL')|lower }}</em></strong>`);
}).attr('data-container', 'body').attr('data-trigger', 'hover').tooltip();
</script>
Expand Down
57 changes: 38 additions & 19 deletions bukuserver/templates/bukuserver/lib.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
{% macro limit_navigation_if_popup() %}
<style>
.popup .navbar-brand {pointer-events: none}
.popup :is(#admin-navbar-collapse, .navbar-toggle) {display: none !important}
.popup .nav-tabs :is(:first-child, :nth-child(2):not(.active)) > * {display: none}
.popup #admin-navbar-collapse {display: block}
.popup :is(.navbar-nav, .navbar-toggler) {display: none}
.popup .nav-tabs :is(:first-child, :nth-child(2)) > .nav-link:not(.active) {display: none}
</style>
<script>opener && (opener !== window) && document.body.classList.add('popup')</script>
{% endmacro %}
Expand All @@ -31,17 +32,37 @@
{% endif %}
{% endmacro %}

{% macro fetch_checkbox(checked=True) %}
{% macro fetch_checkbox(checked=True, modal=False) %}
<script>
$('.admin-form [name=fetch]').remove();
$('.admin-form').append(
$(`<div class="form-group"><label style="display: block"><span class="col-md-2 text-right">{{ _('Fetch') }} &nbsp; </span>`
+`<span class="col-md-10"><input type="checkbox" name="fetch"{% if checked %} checked{% endif %}></span></label></div>`));
$('.admin-form fieldset{% if modal %} .modal-footer{% endif %}').{% if modal %}prepend{% else %}append{% endif %}(
$(`<div class="form-group {{ 'mb-0' if modal else '' }}"{% if modal %} style="flex-grow: 1"{% endif %}>`
+`<label class="control-label {{ 'mb-0' if modal else '' }}">{{ _('Fetch') }} &nbsp; </label>`
+`<input type="checkbox" name="fetch"{% if checked %} checked{% endif %}></div>`));
</script>
{% endmacro %}

{% macro horizontal_form(excluding_popups=False) %}
<script>
$('.admin-form .form-group').each(function () {
if ($('.submit-row', this).length > 0{% if excluding_popups %} || document.body.matches('.popup'){% endif %}) {
$('.submit-row', this).addClass(document.body.matches('.popup') ? 'col-md-12' : 'offset-md-2');
} else if ($('input[type=checkbox], input[type=radio]', this).length > 0) {
$('label', this).addClass('form-row').css({cursor: 'pointer'}).html(`<span>${ $('label', this).html() }</span>`);
$('label span', this).addClass('col-md-2 col-form-label text-right d-inline-block');
$('input', this).appendTo($('label', this));
} else {
$(this).addClass('form-row');
$('input, textarea, select', this).addClass('col-md-10');
$('label', this).addClass('col-md-2 col-form-label text-right');
}
});
</script>
{% endmacro %}

{% macro details_formatting(prefix='') %}
<script>
$(`.modal-header h3`).wrapInner('<h5 class="modal-title">').children(0).unwrap(); // flask-admin #2505
$(`body.popup {{prefix}} a`).attr('target', '_blank');
</script>
{% endmacro %}
Expand Down Expand Up @@ -94,20 +115,18 @@
$(document).ready(function() {
const SIZES = [20, 50, 100]; // hardcoded list; see page_size_form() in admin/model/layout.html
let pageSize = url => new URL(url || location.host).searchParams.get('page_size');
$(`.actions-nav .dropdown-menu`).each(function () {
let _sizes = $(`li a`, this).map(function () {return pageSize(this.href)}).get();
$(`.nav.nav-tabs .dropdown-menu`).each(function () {
let _sizes = $(`a.dropdown-item`, this).map(function () {return pageSize(this.href)}).get();
if (SIZES.every((x, i) => x == _sizes[i]))
$(`li`, this).last().clone().each(function () {
$('a', this).text({{ _('custom')|tojson }}).attr('href', `#`).on('click', () => {
let page = prompt({{ _('Set custom page size (empty for default)')|tojson }}, pageSize(location) || '');
if ((page == "") || (Number(page) >= 1)) {
let search = new URL(location).searchParams;
(page ? search.set('page_size', parseInt(page)) : search.delete('page_size'));
location.search = search;
} else if (page != null)
alert({{ _('Invalid page size')|tojson }} + `: "${page}"`);
return false;
});
$('a', this).last().clone().text({{ _('custom')|tojson }}).attr('href', `#`).on('click', () => {
let page = prompt({{ _('Set custom page size (empty for default)')|tojson }}, pageSize(location) || '');
if (Number(page) || (page == "")) {
let search = new URL(location).searchParams;
(page ? search.set('page_size', page) : search.delete('page_size'));
location.search = search;
} else if (page != null)
alert({{ _('Invalid page size')|tojson }} + `: "${page}"`);
return false;
}).appendTo(this);
})
});
Expand Down
Loading

0 comments on commit dd05421

Please sign in to comment.