Skip to content

Commit 95aa6fc

Browse files
authored
Merge branch 'main' into phone-field
2 parents 63ea7cb + 8eee661 commit 95aa6fc

File tree

8 files changed

+148
-38
lines changed

8 files changed

+148
-38
lines changed

package-lock.json

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ViewNamespaces.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,19 @@ public static function getWithFallbackFor(string $domain, string $viewNamespaces
7272

7373
return array_unique(array_merge($viewNamespacesFromConfig, self::getFor($domain)));
7474
}
75+
76+
/**
77+
* This is an helper function that returns the view namespace with the view name appended.
78+
* It's usefull to use in blade templates with `@includeFirst(ViewNamespaces::getViewPathsFor('columns', 'some_column'))`.
79+
*
80+
* @param string $domain
81+
* @param string $viewName
82+
* @return array
83+
*/
84+
public static function getViewPathsFor(string $domain, string $viewName)
85+
{
86+
return array_map(function ($item) use ($viewName) {
87+
return $item.'.'.$viewName;
88+
}, self::getFor($domain));
89+
}
7590
}

src/app/Library/CrudPanel/Traits/Fields.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public function makeFirstField()
162162
return false;
163163
}
164164

165-
$firstField = array_keys(array_slice($this->getCleanFields(), 0, 1))[0];
165+
$firstField = array_keys(array_slice($this->getCleanStateFields(), 0, 1))[0];
166166
$this->beforeField($firstField);
167167
}
168168

src/app/Library/CrudPanel/Traits/Query.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,30 @@ private function getCountFromQuery(Builder $query)
242242
// add the count query in the "outer" query.
243243
$outerQuery = $outerQuery->selectRaw('count(*) as total_rows');
244244

245+
// Expression columns are hand-written by developers in ->selectRaw() and we can't exclude those statements reliably
246+
// so we just store them and re-use them in the sub-query too.
247+
$expressionColumns = [];
248+
249+
foreach ($crudQuery->columns as $column) {
250+
if (! is_string($column) && is_a($column, 'Illuminate\Database\Query\Expression')) {
251+
$expressionColumns[] = $column;
252+
}
253+
}
245254
// add the subquery from where the "outer query" will count the results.
246255
// this subquery is the "main crud query" without some properties:
247256
// - columns : we manually select the "minimum" columns possible from database.
248257
// - orders/limit/offset because we want the "full query count" where orders don't matter and limit/offset would break the total count
249258
$subQuery = $crudQuery->cloneWithout(['columns', 'orders', 'limit', 'offset']);
250259

251-
$outerQuery = $outerQuery->fromSub($subQuery->select($modelTable.'.'.$this->model->getKeyName()), $modelTable.'_aggregator');
260+
// select only one column for the count
261+
$subQuery->select($modelTable.'.'.$this->model->getKeyName());
262+
263+
// in case there are raw expressions we need to add them too.
264+
foreach ($expressionColumns as $expression) {
265+
$subQuery->selectRaw($expression);
266+
}
267+
268+
$outerQuery = $outerQuery->fromSub($subQuery, str_replace('.', '_', $modelTable).'_aggregator');
252269

253270
return $outerQuery->cursor()->first()->total_rows;
254271
}

src/resources/views/crud/fields/password.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<input
1414
type="password"
1515
name="{{ $field['name'] }}"
16+
value="{{ $field['value'] ?? $field['default'] ?? '' }}"
1617
@include('crud::fields.inc.attributes')
1718
>
1819

src/resources/views/crud/form_content.blade.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,8 @@ function preventUnload(event) {
119119
@endif
120120
121121
// Save button has multiple actions: save and exit, save and edit, save and new
122-
var saveActions = $('#saveActions'),
123-
crudForm = saveActions.parents('form'),
124-
saveActionField = $('[name="_save_action"]');
125-
126-
saveActions.on('click', '.dropdown-menu a', function(){
127-
var saveAction = $(this).data('value');
128-
saveActionField.val( saveAction );
129-
crudForm.submit();
130-
});
122+
var saveActions = $('#saveActions')
123+
crudForm = saveActions.parents('form')
131124
132125
// Ctrl+S and Cmd+S trigger Save button click
133126
$(document).keydown(function(e) {

src/resources/views/crud/inc/form_fields_script.blade.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
class CrudField {
1010
constructor(name) {
1111
this.name = name;
12-
13-
// get the input/textarea/select that has that field name
14-
this.$input = $(`input[name="${this.name}"], textarea[name="${this.name}"], select[name="${this.name}"], select[name="${this.name}[]"]`).first();
15-
12+
// get the current input
13+
this.$input = this.activeInput;
1614
// get the field wraper
1715
this.wrapper = this.inputWrapper;
1816
@@ -36,6 +34,13 @@ class CrudField {
3634
3735
}
3836
37+
get activeInput() {
38+
// get the input/textarea/select that has that field name
39+
this.$input = $(`input[name="${this.name}"], textarea[name="${this.name}"], select[name="${this.name}"], select[name="${this.name}[]"]`);
40+
let possibleInput = this.$input.length === 1 ? this.$input : this.$input.filter(function() { return $(this).closest('[id=inline-create-dialog]').length });
41+
return possibleInput.length === 1 ? possibleInput : this.$input.first();
42+
}
43+
3944
get mainInput() {
4045
let input = this.wrapper.find('[bp-field-main-input]').first();
4146

src/resources/views/crud/inc/form_save_buttons.blade.php

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
<div class="btn-group" role="group">
1515
@if(!empty($saveAction['options']))
16-
<button id="btnGroupDrop1" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span><span class="sr-only">&#x25BC;</span></button>
17-
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
18-
@foreach( $saveAction['options'] as $value => $label)
19-
<button type="submit" class="dropdown-item" data-value="{{ $value }}">{{ $label }}</a>
20-
@endforeach
16+
<button id="bpSaveButtonsGroup" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span><span class="sr-only">&#x25BC;</span></button>
17+
<div class="dropdown-menu" aria-labelledby="bpSaveButtonsGroup">
18+
@foreach( $saveAction['options'] as $value => $label)
19+
<button type="submit" class="dropdown-item" data-value="{{ $value }}">{{ $label }}</button>
20+
@endforeach
2121
</div>
2222
@endif
2323
</div>
@@ -33,3 +33,82 @@
3333
</div>
3434
@endif
3535

36+
@push('after_scripts')
37+
<script>
38+
39+
// this function checks if form is valid.
40+
function checkFormValidity(form) {
41+
// the condition checks if `checkValidity` is defined in the form (browser compatibility)
42+
if (form[0].checkValidity) {
43+
return form[0].checkValidity();
44+
}
45+
return false;
46+
}
47+
48+
// this function checks if any of the inputs has errors and report them on page.
49+
// we use it to report the errors after form validation fails and making the error fields visible
50+
function reportValidity(form) {
51+
// the condition checks if `reportValidity` is defined in the form (browser compatibility)
52+
if (form[0].reportValidity) {
53+
// hide the save actions drop down if open
54+
$('#saveActions').find('.dropdown-menu').removeClass('show');
55+
// validate and display form errors
56+
form[0].reportValidity();
57+
}
58+
}
59+
60+
function changeTabIfNeededAndDisplayErrors(form) {
61+
// we get the first erroed field
62+
var $firstErrorField = form.find(":invalid").first();
63+
// we find the closest tab
64+
var $closestTab = $($firstErrorField).closest('.tab-pane');
65+
// if we found the tab we will change to that tab before reporting validity of form
66+
if($closestTab.length) {
67+
var id = $closestTab.attr('id');
68+
// switch tabs
69+
$('.nav a[href="#' + id + '"]').tab('show');
70+
}
71+
reportValidity(form);
72+
}
73+
74+
// make all submit buttons trigger HTML5 validation
75+
jQuery(document).ready(function($) {
76+
77+
var selector = $('#bpSaveButtonsGroup').next();
78+
var form = $(selector).closest('form');
79+
var saveActionField = $('[name="save_action"]');
80+
var $defaultSubmitButton = $(form).find(':submit');
81+
// this is the main submit button, the default save action.
82+
$($defaultSubmitButton).on('click', function(e) {
83+
e.preventDefault();
84+
$saveAction = $(this).children('span').eq(1);
85+
// if form is valid just submit it
86+
if(checkFormValidity(form)) {
87+
saveActionField.val( $saveAction.attr('data-value') );
88+
form.submit();
89+
}else{
90+
// navigate to the tab where the first error happens
91+
changeTabIfNeededAndDisplayErrors(form);
92+
}
93+
});
94+
95+
//this is for the anchors AKA other non-default save actions.
96+
$(selector).find('a').each(function() {
97+
$(this).click(function(e) {
98+
//we check if form is valid
99+
if (checkFormValidity(form)) {
100+
//if everything is validated we proceed with the submission
101+
var saveAction = $(this).data('value');
102+
saveActionField.val( saveAction );
103+
form.submit();
104+
}else{
105+
// navigate to the tab where the first error happens
106+
changeTabIfNeededAndDisplayErrors(form);
107+
}
108+
e.stopPropagation();
109+
});
110+
});
111+
});
112+
113+
</script>
114+
@endpush

0 commit comments

Comments
 (0)