Skip to content

Commit 6229af0

Browse files
authored
Merge pull request #3 from CosminBd/main
Added Detail view support
2 parents a0a9897 + ce8bf66 commit 6229af0

9 files changed

+183
-33
lines changed

README.md

+29-6
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function fields(Request $request)
3636

3737
// ... Nova default fields
3838

39-
ActionButton::make('') // Name in resource table column
39+
ActionButton::make('') // Name in resource table column
4040
->icon('<svg></svg>') // Svg icon (optional)
4141
->title('Refresh') // Title (optional)
4242
->styles([]) // Custom css styles (optional)
@@ -63,11 +63,11 @@ public function fields(Request $request)
6363

6464
// ... Nova default fields
6565

66-
ActionButtons::make()->collection([
67-
ActionButton::make('')->action(),
68-
ActionButton::make('')->action(),
69-
ActionButton::make('')->action(),
70-
])
66+
ActionButtons::make()->collection([
67+
ActionButton::make('')->action(),
68+
ActionButton::make('')->action(),
69+
ActionButton::make('')->action(),
70+
])
7171

7272
// ... Nova default fields
7373
];
@@ -123,6 +123,29 @@ class RefreshAction extends Action
123123

124124
```
125125

126+
> This field is available on index and detail (Thanks to [@CosminBd](https://github.com/CosminBd)) views
127+
128+
---
129+
To hide the action either on Index or Detail, you can add the methods in the action declaration as per:
130+
```php
131+
ActionButton::make('My action')
132+
->action((new RefreshAction)->onlyOnDetail(), $this->resource?->id)
133+
->icon('')
134+
->asToolbarButton(),
135+
```
136+
This is available for both action buttons and action button groups, and it works in individual actions which are part of the action group.
137+
138+
---
139+
140+
To run actions without confirmation, you can add the `$withoutConfirmation = true` property to the Laravel Nova action or provide it as a method when you declare the action button
141+
```php
142+
ActionButton::make('My action')
143+
->action((new RefreshAction)->withoutConfirmation(), $this->resource?->id)
144+
->icon('')
145+
->asToolbarButton(),
146+
```
147+
148+
126149
## License
127150

128151
This project is open-sourced software licensed under the [MIT license](LICENSE.md).

dist/js/field.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/js/components/ActionButtonField.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div>
3-
<a href="#" :style="finalStyles" :class="finalClasses" @click.stop.prevent="fireAction">
3+
<a href="#" :style="finalStyles" :class="finalClasses" :title="name" @click.stop.prevent="fireAction">
44
<span v-if="text" v-text="text"/>
55
<span v-if="icon" v-html="icon"/>
66
</a>
@@ -32,7 +32,8 @@
3232
3333
// Computed
3434
const text = computed(() => props?.field?.text || null);
35-
const icon = computed(() => props?.field?.icon || null)
35+
const icon = computed(() => props?.field?.icon || null);
36+
const name = computed(() => props?.field?.name || null);
3637
const customStyles = computed(() => props?.field?.styles || []);
3738
const customClasses = computed(() => props?.field?.classes || []);
3839
const asToolbarButton = computed(() => props?.field?.asToolbarButton === true);
@@ -60,6 +61,7 @@
6061
}));
6162
6263
const selectedAction = computed(() => props?.field?.action);
64+
6365
const selectedResources = computed(() => [props?.field?.resourceId]);
6466
6567
// Bindings

resources/js/components/ActionButtonsField.vue

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
// Computed
2525
const collection = computed(() => props?.field?.collection || []);
2626
const actions = computed(() => (collection?.value || [])
27+
.filter((field) => field.action.showOnIndex)
2728
.map(field => ({
2829
field: field,
2930
queryString: props?.queryString,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<template>
2+
<panel-item :field="field">
3+
<template v-slot:value>
4+
<a href="#" :style="finalStyles" :class="finalClasses" :title="name" @click.stop.prevent="fireAction">
5+
<span v-if="text" v-text="text"/>
6+
<span v-if="icon" v-html="icon"/>
7+
</a>
8+
9+
<!-- Action Confirmation Modal -->
10+
<portal to="modals" transition="fade-transition">
11+
<component
12+
v-if="confirmActionModalOpened"
13+
v-bind="options"
14+
class="text-left"
15+
:is="selectedAction.component"
16+
@close="closeConfirmationModal"
17+
@confirm="executeAction">
18+
</component>
19+
</portal>
20+
</template>
21+
</panel-item>
22+
</template>
23+
24+
<script setup>
25+
26+
// Vue
27+
import {computed} from 'vue';
28+
29+
// Props
30+
const props = defineProps({
31+
field: {type: Object, default: null},
32+
queryString: {type: Object, default: null},
33+
resourceName: {type: String, default: null},
34+
});
35+
36+
// Composables
37+
import {useHandleAction} from '../mixins/HandlesActions'
38+
39+
// Computed
40+
const text = computed(() => props?.field?.text || null);
41+
const icon = computed(() => props?.field?.icon || null);
42+
const name = computed(() => props?.field?.name || null);
43+
const customStyles = computed(() => props?.field?.styles || []);
44+
const customClasses = computed(() => props?.field?.classes || []);
45+
const asToolbarButton = computed(() => props?.field?.asToolbarButton === true);
46+
47+
const actionButtonClasses = computed(() => [
48+
'flex-shrink-0', 'shadow', 'rounded', 'focus:outline-none', 'ring-primary-200', 'dark:ring-gray-600',
49+
'focus:ring', 'bg-primary-500', 'hover:bg-primary-400', 'active:bg-primary-600',
50+
'text-white', 'dark:text-gray-800', 'inline-flex', 'items-center', 'font-bold', 'px-2', 'h-9', 'text-sm', 'flex-shrink-0',
51+
])
52+
const toolbarButtonClasses = computed(() => [
53+
'toolbar-button', 'hover:text-primary-500', 'px-2', 'v-popper--has-tooltip', 'w-10'
54+
])
55+
56+
const finalStyles = computed(() => ({...(customStyles.value || {})}))
57+
const finalClasses = computed(() => [...(asToolbarButton?.value === true ? toolbarButtonClasses.value : actionButtonClasses.value), ...(customClasses.value || [])])
58+
59+
const queryString = computed(() => ({
60+
action: selectedAction?.value?.uriKey,
61+
search: props?.queryString?.currentSearch,
62+
filters: props?.queryString?.encodedFilters,
63+
trashed: props?.queryString?.currentTrashed,
64+
viaResource: props?.queryString?.viaResource,
65+
viaResourceId: props?.queryString?.viaResourceId,
66+
viaRelationship: props?.queryString?.viaRelationship,
67+
}));
68+
69+
const selectedAction = computed(() => props?.field?.action);
70+
71+
const selectedResources = computed(() => [props?.field?.resourceId]);
72+
73+
// Bindings
74+
const {
75+
errors,
76+
working,
77+
fireAction,
78+
executeAction,
79+
closeConfirmationModal,
80+
confirmActionModalOpened
81+
} = useHandleAction(
82+
{
83+
queryString: queryString.value,
84+
resourceName: props?.resourceName,
85+
selectedAction: selectedAction.value,
86+
selectedResources: selectedResources.value,
87+
}
88+
)
89+
90+
// Computed
91+
const options = computed(() => ({
92+
show: true,
93+
errors: errors?.value,
94+
action: selectedAction?.value,
95+
working: working?.value === true,
96+
resourceName: props?.resourceName,
97+
selectedResources: selectedResources?.value,
98+
}))
99+
100+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<panel-item :field="field">
3+
<template v-slot:value>
4+
<div class="flex items-center ">
5+
<template v-for="(action, k) in actions" :key="k">
6+
<action-button v-bind="action"/>
7+
</template>
8+
</div>
9+
</template>
10+
</panel-item>
11+
</template>
12+
13+
<script setup>
14+
// Vue
15+
import {computed} from 'vue';
16+
17+
// Components
18+
import ActionButton from './ActionButtonField';
19+
20+
// Props
21+
const props = defineProps({
22+
field: {type: Object, default: null},
23+
queryString: {type: Object, default: null},
24+
resourceName: {type: String, default: null},
25+
});
26+
27+
// Computed
28+
const collection = computed(() => props?.field?.collection || []);
29+
const actions = computed(() => (collection?.value || [])
30+
.filter((field) => field.action.showOnDetail)
31+
.map(field => ({
32+
field: field,
33+
queryString: props?.queryString,
34+
resourceName: props?.resourceName,
35+
})));
36+
37+
</script>

resources/js/field.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import ActionButtonField from './components/ActionButtonField'
2-
import ActionButtonsField from './components/ActionButtonsField'
1+
import ActionButtonField from './components/ActionButtonField';
2+
import ActionButtonsField from './components/ActionButtonsField';
3+
import DetailActionButtonField from './components/DetailActionButtonField.vue';
4+
import DetailActionButtonsField from './components/DetailActionButtonsField.vue';
35

46
Nova.booting((app, store) => {
5-
app.component('index-action-button', ActionButtonField)
6-
app.component('index-action-buttons', ActionButtonsField)
7-
})
7+
app.component('index-action-button', ActionButtonField);
8+
app.component('detail-action-button', DetailActionButtonField);
9+
app.component('index-action-buttons', ActionButtonsField);
10+
app.component('detail-action-buttons', DetailActionButtonsField);
11+
});

src/ActionButton.php

+1-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ActionButton extends Field
1414
*/
1515
public $component = 'action-button';
1616

17-
17+
1818
/**
1919
* Indicates if the element should be shown on the update view.
2020
*
@@ -23,14 +23,6 @@ class ActionButton extends Field
2323
public $showOnUpdate = false;
2424

2525

26-
/**
27-
* Indicates if the element should be shown on the detail view.
28-
*
29-
* @var bool
30-
*/
31-
public $showOnDetail = false;
32-
33-
3426
/**
3527
* Indicates if the element should be shown on the creation view.
3628
*
@@ -51,7 +43,6 @@ public function action(Action $action, mixed $resourceId): self
5143
]);
5244
}
5345

54-
5546
/**
5647
* Text inside button.
5748
*

src/ActionButtons.php

+1-9
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class ActionButtons extends Field
1313
*/
1414
public $component = 'action-buttons';
1515

16-
16+
1717
/**
1818
* The text alignment for the field's text in tables.
1919
*
@@ -30,14 +30,6 @@ class ActionButtons extends Field
3030
public $showOnUpdate = false;
3131

3232

33-
/**
34-
* Indicates if the element should be shown on the detail view.
35-
*
36-
* @var bool
37-
*/
38-
public $showOnDetail = false;
39-
40-
4133
/**
4234
* Indicates if the element should be shown on the creation view.
4335
*

0 commit comments

Comments
 (0)