Skip to content

Commit

Permalink
Basic implementation of batch actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierstoval committed Sep 15, 2024
1 parent 24d7717 commit 7ea53b6
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 23 deletions.
11 changes: 8 additions & 3 deletions src/lib/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,19 @@ export abstract class DefaultAction implements Action {

/** */
export class CallbackAction extends DefaultAction {
private readonly _callback: (item?: object | undefined) => void;
private readonly _callback: (item?: unknown) => void;

constructor(
label: string,
icon: Optional<ActionIcon>,
callback: (item?: object | undefined) => void,
callback: (item?: unknown) => void,
options?: ActionOptions
) {
super(label, icon, options);
this._callback = callback;
}

public call(item?: object | undefined): unknown {
public call(item?: unknown): unknown {
return this._callback.call(null, item);
}
}
Expand All @@ -79,6 +79,11 @@ export class UrlAction extends DefaultAction {
item: object & { [key: string]: string | number | boolean } = {},
identifierFieldName: string = 'id'
): string {
if (Array.isArray(item)) {
console.warn('Provided item for UrlAction is an array, and arrays are not supported. Using the first item of the array, or an empty object if not set.');
item = item[0] ?? {};
}

let url = this._url || '';

const mightNeedId = item[identifierFieldName] !== undefined;
Expand Down
8 changes: 4 additions & 4 deletions src/lib/Crud/Operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface CrudOperation {
/** */ readonly label: string;
/** */ readonly displayComponentName: CrudTheme;
/** */ readonly fields: Array<FieldInterface<FieldOptions>>;
/** */ readonly actions: Array<Action>;
/** */ readonly contextActions: Array<Action>;
/** */ readonly options: Record<string, string | unknown>;

/** */
Expand Down Expand Up @@ -75,7 +75,7 @@ export abstract class BaseCrudOperation implements CrudOperation {
/** */ public readonly label: string,
/** */ public readonly displayComponentName: CrudTheme,
/** */ public readonly fields: Array<FieldInterface<FieldOptions>>,
/** */ public readonly actions: Array<Action>,
/** */ public readonly contextActions: Array<Action>,
/** */ public readonly options: Record<string, string | unknown> = {}
) {}

Expand Down Expand Up @@ -175,14 +175,14 @@ export class List extends BaseCrudOperation {
/** */
constructor(
fields: Array<FieldInterface<FieldOptions>>,
actions: Array<Action> = [],
itemsActions: Array<Action> = [],
options: Partial<ListOperationOptions> = {}
) {
options.globalActions ??= [];
options.batchActions ??= [];
options.pagination = { ...defaultPaginationOptions(), ...(options.pagination || {}) };
options.filters ??= [];
super('list', 'crud.list.label', 'list', fields, actions, options);
super('list', 'crud.list.label', 'list', fields, itemsActions, options);
this.options = options as ListOperationOptions;
}
}
Expand Down
12 changes: 5 additions & 7 deletions src/lib/themes/svelte/carbon/Crud/CrudList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
let page: number | undefined;
const configuredFilters = operation.options?.filters || [];
const actions = operation.actions;
const actions = operation.contextActions;
const sortableDataTable =
operation.fields.filter((field: BaseField<FieldOptions>) => !field.options?.sortable).length >
0;
Expand All @@ -42,13 +42,14 @@
let showPagination = operation.options.pagination.enabled;
let rows: Promise<unknown>;
let paginator: PaginatedResults<unknown> | undefined;
let globalActions: Array<Action> = [];
let globalActions: Array<Action> = operation.options.globalActions || [];
let batchActions: Array<Action> = operation.options.batchActions || [];
if (!crud.options.stateProvider) {
throw new Error(`No StateProvider was given to the "${crud.name}" CRUD.`);
}
if ((!operation) instanceof List) {
if (!(operation instanceof List)) {
throw new Error(
'CrudList view can only accept operations that are instances of the List operation.'
);
Expand Down Expand Up @@ -113,10 +114,6 @@
});
}
if (operation instanceof List<unknown> || operation.options?.globalActions?.length) {
globalActions = operation.options.globalActions;
}
function onPaginationUpdate(event: CustomEvent<{ page: number; pageSize: number }>) {
page = event.detail.page;
requestParameters.page = event.detail.page;
Expand Down Expand Up @@ -159,6 +156,7 @@
{rows}
{actions}
{globalActions}
{batchActions}
{page}
{operation}
{onSort}
Expand Down
41 changes: 36 additions & 5 deletions src/lib/themes/svelte/carbon/DataTable/DataTable.svelte
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { _ } from 'svelte-i18n';
import DataTable from 'carbon-components-svelte/src/DataTable/DataTable.svelte';
import DataTable, {type DataTableRowId} from 'carbon-components-svelte/src/DataTable/DataTable.svelte';
import DataTableSkeleton from 'carbon-components-svelte/src/DataTable/DataTableSkeleton.svelte';
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import Loading from 'carbon-components-svelte/src/Loading/Loading.svelte';
import Toolbar from 'carbon-components-svelte/src/DataTable/Toolbar.svelte';
import ToolbarBatchActions from 'carbon-components-svelte/src/DataTable/ToolbarBatchActions.svelte';
import DataTableToolbar from '$lib/themes/svelte/carbon/DataTable/Toolbar/DataTableToolbar.svelte';
import ItemActions from '$lib/themes/svelte/carbon/DataTable/actions/ItemActions.svelte';
import type { Headers, Row, Rows } from '$lib/DataTable';
import type { Action } from '$lib/Actions';
import type { FilterInterface, FilterOptions } from '$lib/Filter';
import type { ThemeConfig } from '$lib/types';
import type { SubmittedData } from '$lib/Crud/Form';
import {type FieldInterface, type FieldOptions, TextField} from "$lib";
import ToolbarAction from "$lib/themes/svelte/carbon/DataTable/Toolbar/ToolbarAction.svelte";
export let headers: Headers = [];
export let rows: Promise<Rows>;
export let actions: Action[] = [];
export let globalActions: Array<Action> = [];
export let batchActions: Action[] = [];
export let filters: Array<FilterInterface<FilterOptions>> = [];
export let page: number | undefined;
export let theme: ThemeConfig;
export let sortable: boolean;
export let onSort: () => unknown | undefined;
let actionsCellIndex = -1;
let batchSelectionIsActive = false;
let selectedRowIds: ReadonlyArray<DataTableRowId> = [];
if (actions.length) {
headers.push({
Expand All @@ -34,17 +42,16 @@
actionsCellIndex = headers.length - 1;
}
function getFieldFromRow(fieldName: string, row: Row) {
function getFieldFromRow(fieldName: string, row: Row): FieldInterface<FieldOptions> {
if (!row.__crud_operation) {
console.error('Internal "__crud_operation" property isn\'t properly injected.');
return theme.viewFields.default;
throw new Error('Internal "__crud_operation" property isn\'t properly injected.');
}
const matchingFields = row.__crud_operation.fields.filter((f) => f.name === fieldName);
if (!matchingFields.length) {
console.warn(`Field "${fieldName}" was not found in current operation.`);
return theme.viewFields.default;
return new TextField(fieldName, fieldName);
}
if (matchingFields.length > 1) {
Expand All @@ -71,6 +78,11 @@
function onFiltersSubmit(event: CustomEvent<SubmittedData>) {
dispatchEvent('submitFilters', event.detail);
}
function onCancelSelection(event: CustomEvent<null>) {
event.preventDefault();
batchSelectionIsActive = false;
}
</script>

{#await rows}
Expand All @@ -81,9 +93,12 @@
{page}
{sortable}
zebra
selectable={batchActions.length > 0}
batchSelection={batchSelectionIsActive}
rows={resolvedRows}
size="short"
on:click:header={onSort}
bind:selectedRowIds
{...$$restProps}
>
<svelte:fragment slot="title">
Expand All @@ -100,6 +115,22 @@
on:submitFilters={onFiltersSubmit}
/>
{/if}
{#if batchActions.length > 0}
<Toolbar>
<ToolbarBatchActions
bind:active={batchSelectionIsActive}
on:cancel={onCancelSelection}
>
{#each batchActions as action}
<ToolbarAction {action} action_arguments={[selectedRowIds]} />
{/each}
</ToolbarBatchActions>
<!--<ToolbarContent>-->
<!-- <Button on:click={() => (active = true)}>Edit rows</Button>-->
<!--</ToolbarContent>-->
</Toolbar>
{/if}

{#if !resolvedRows.length}
<InlineNotification kind="warning" hideCloseButton={true} lowContrast={true}>
{$_('error.crud.list.no_elements')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import FilterReset from 'carbon-icons-svelte/lib/FilterReset.svelte';
import type { Action } from '$lib/Actions';
import ActionComponent from '$lib/themes/svelte/carbon/DataTable/Toolbar/ToolbarAction.svelte';
import ToolbarAction from '$lib/themes/svelte/carbon/DataTable/Toolbar/ToolbarAction.svelte';
import FilterComponent from '$lib/themes/svelte/carbon/DataTable/Toolbar/ToolbarFilter.svelte';
import type { Filter, FilterOptions } from '$lib/Filter';
import type { ThemeConfig } from '$lib/types';
Expand Down Expand Up @@ -58,7 +58,7 @@
<Toolbar>
<ToolbarContent>
{#each actions as action}
<ActionComponent {action} />
<ToolbarAction {action} />
{/each}
</ToolbarContent>
</Toolbar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
import { type Action, CallbackAction, UrlAction } from '$lib/Actions';
export let action: Action;
export let action_arguments: Array<unknown> = [];
</script>

{#if action instanceof UrlAction}
<Button
href={action.url()}
href={action.url(...action_arguments) || ''}
icon={action.icon}
kind={action.options?.buttonKind}
{...action.options.htmlAttributes}
Expand All @@ -20,7 +21,7 @@
</Button>
{:else if action instanceof CallbackAction}
<Button
on:click={async () => await action.call()}
on:click={async () => await action.call(...action_arguments)}
icon={action.icon}
kind={action.options?.buttonKind}
{...action.options.htmlAttributes}
Expand Down
18 changes: 18 additions & 0 deletions src/testApp/TestCrud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ export const testCrud = new CrudDefinition<Test>({
),
new UrlAction('New', '/admin/tests/new', Pen)
],
batchActions: [
new CallbackAction(
'Testing batch actions',
ViewIcon,
function (item?: unknown) {
if (!Array.isArray(item)) {
if (item) {
console.warn('Item used for Callback action is not an array.');
}
item = [];
}
if (!item) {
item = [];
}
success("Selected IDs to process:\n" + (item as Array<string>).join("\n"));
},
),
],
filters: [
new TextFilter('text_field', 'Filter text'),
new BooleanFilter('checkbox_field', 'Filter checkbox'),
Expand Down

0 comments on commit 7ea53b6

Please sign in to comment.