Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial attempt for Tabler Template Mode #2444

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions flask_admin/static/admin/js/tabler_filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
This file is from Flask-Admin project with a slight modification to work with Tabler.
*/
var AdminFilters = function(element, filtersElement, filterGroups, activeFilters) {
var $root = $(element);
var $container = $('.filters', $root);
var lastCount = 0;

function getCount(name) {
var idx = name.indexOf('_');

if (idx === -1) {
return 0;
}

return parseInt(name.substr(3, idx - 3), 10);
}

function makeName(name) {
var result = 'flt' + lastCount + '_' + name;
lastCount += 1;
return result;
}

function removeFilter() {
$(this).closest('tr').remove();
if($('.filters tr').length == 0) {
$('button', $root).hide();
$('a[class=btn]', $root).hide();
$('.filters tbody').remove();
} else {
$('button', $root).show();
}

return false;
}

// triggered when the filter operation (equals, not equals, etc) is changed
function changeOperation(subfilters, $el, filter, $select) {
// get the filter_group subfilter based on the index of the selected option
var selectedFilter = subfilters[$select.select2('data').element[0].index];
var $inputContainer = $el.find('td').last();

// recreate and style the input field (turn into date range or select2 if necessary)
var $field = createFilterInput($inputContainer, null, selectedFilter);
styleFilterInput(selectedFilter, $field);

$('button', $root).show();
}

// generate HTML for filter input - allows changing filter input type to one with options or tags
function createFilterInput(inputContainer, filterValue, filter) {
if (filter.type == "select2-tags") {
var $field = $('<input type="hidden" class="filter-val form-control" />').attr('name', makeName(filter.arg));
$field.val(filterValue);
} else if (filter.options) {
var $field = $('<select class="filter-val" />').attr('name', makeName(filter.arg));

$(filter.options).each(function() {
// for active filter inputs with options, add "selected" if there is a matching active filter
if (filterValue && (filterValue == this[0])) {
$field.append($('<option/>')
.val(this[0]).text(this[1]).attr('selected', true));
} else {
$field.append($('<option/>')
.val(this[0]).text(this[1]));
}
});
} else {
var $field = $('<input type="text" class="filter-val form-control" />').attr('name', makeName(filter.arg));
$field.val(filterValue);
}
inputContainer.replaceWith($('<td/>').append($field));

return $field;
}

// add styling to input field, accommodates filters that change the input field's HTML
function styleFilterInput(filter, field) {
if (filter.type) {
if ((filter.type == "datepicker") || (filter.type == "daterangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD");
} else if ((filter.type == "datetimepicker") || (filter.type == "datetimerangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD HH:mm:ss");
} else if ((filter.type == "timepicker") || (filter.type == "timerangepicker")) {
field.attr('data-date-format', "HH:mm:ss");
} else if (filter.type == "select2-tags") {
var options = [];
if (filter.options) {
filter.options.forEach(function(option) {
options.push({id:option[0], text:option[1]});
});
// save tag options as json on data attribute
field.attr('data-tags', JSON.stringify(options));
}
}
faForm.applyStyle(field, filter.type);
} else if (filter.options) {
filter.type = "select2";
faForm.applyStyle(field, filter.type);
}

return field;
}

function addFilter(name, subfilters, selectedIndex, filterValue) {
var $el = $('<tr class="form-horizontal" />').appendTo($container);

// Filter list
$el.append(
$('<td/>').append(
$('<a href="#" class="btn btn-secondary remove-filter" />')
.append($('<span class="close-icon">&times;</span>'))
.append('&nbsp;')
.append(name)
.click(removeFilter)
)
);

// Filter operation <select> (equal, not equal, etc)
var $select = $('<select class="filter-op" />');

// if one of the subfilters are selected, use that subfilter to create the input field
var filterSelection = 0;
$.each(subfilters, function( subfilterIndex, subfilter ) {
if (this.index == selectedIndex) {
$select.append($('<option/>').attr('value', subfilter.arg).attr('selected', true).text(subfilter.operation));
filterSelection = subfilterIndex;
} else {
$select.append($('<option/>').attr('value', subfilter.arg).text(subfilter.operation));
}
});

$el.append(
$('<td/>').append($select)
);

// select2 for filter-op (equal, not equal, etc)
$select.select2({width: 'resolve'}).on("change", function(e) {
changeOperation(subfilters, $el, filter, $select);
});

// get filter option from filter_group, only for new filter creation
var filter = subfilters[filterSelection];
var $inputContainer = $('<td/>').appendTo($el);

var $newFilterField = createFilterInput($inputContainer, filterValue, filter).focus();
var $styledFilterField = styleFilterInput(filter, $newFilterField);

return $styledFilterField;
}

// Add Filter Button, new filter
$('a.filter', filtersElement).click(function() {
var name = ($(this).text().trim !== undefined ? $(this).text().trim() : $(this).text().replace(/^\s+|\s+$/g,''));

addFilter(name, filterGroups[name], false, null);

$('button', $root).show();
});

// on page load - add active filters
$.each(activeFilters, function( activeIndex, activeFilter ) {
var idx = activeFilter[0],
name = activeFilter[1],
filterValue = activeFilter[2];
var $activeField = addFilter(name, filterGroups[name], idx, filterValue);
});

// show "Apply Filter" button when filter input is changed
$('.filter-val', $root).on('input change', function() {
$('button', $root).show();
});

$('.remove-filter', $root).click(removeFilter);

$('.filter-val', $root).not('.select2-container').each(function() {
var count = getCount($(this).attr('name'));
if (count > lastCount)
lastCount = count;
});

lastCount += 1;
};

(function($) {
$(document).on('adminFormReady', function(evt){
if ($('#filter-groups-data').length == 1) {
var filter = new AdminFilters(
'#filter_form', '.field-filters',
JSON.parse($('#filter-groups-data').text()),
JSON.parse($('#active-filters-data').text())
);
}
});
$(document).trigger('adminFormReady'); // trigger event to allow dynamic filter form to function properly
})(jQuery);
12 changes: 12 additions & 0 deletions flask_admin/static/admin/js/tabler_modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
This file is from Flask-Admin project with a slight modification to work with Tabler.
*/
// fixes "content does not load remote modal on clicked modal button"
$('body').on('click.modal.data-api', '[data-bs-toggle="modal"]', function () {
$($(this).data("bs-target") + ' .modal-content').load($(this).attr('href'));
});

$(function() {
// Apply flask-admin form styles after the modal is loaded
window.faForm.applyGlobalStyles(document);
});
39 changes: 39 additions & 0 deletions flask_admin/templates/tabler/actions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% import "admin/static.html" as admin_static with context %}
{% macro dropdown(actions, btn_class="nav-link dropdown-toggle") -%}
<a class="{{ btn_class }}"
data-bs-toggle="dropdown"
href="javascript:void(0)"
role="button"
aria-haspopup="true"
aria-expanded="false">{{ _gettext("With selected") }}</a>
<div class="dropdown-menu">
{% for p in actions %}
<a class="dropdown-item"
href="javascript:void(0)"
onclick="return modelActions.execute('{{ p[0] }}');">{{ _gettext(p[1]) }}</a>
{% endfor %}
</div>
{% endmacro %}
{% macro form(actions, url) %}
{% if actions %}
<form id="action_form"
action="{{ url }}"
method="post"
style="display: none">
{% if action_form.csrf_token %}
{{ action_form.csrf_token }}
{% elif csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
{% endif %}
{{ action_form.url(value=return_url) }}
{{ action_form.action() }}
</form>
{% endif %}
{% endmacro %}
{% macro script(message, actions, actions_confirmation) %}
{% if actions %}
<div id="actions-confirmation-data" style="display:none;">{{ actions_confirmation|tojson|safe }}</div>
<div id="message-data" style="display:none;">{{ message|tojson|safe }}</div>
<script src="{{ admin_static.url(filename='admin/js/actions.js', v='1.0.0') }}"></script>
{% endif %}
{% endmacro %}
Loading
Loading