Skip to content

Commit 9c13de6

Browse files
authored
Add methods and callbacks in the Action List control (#355)
* Add support to removeItem method in the Action List control * Add "fixItemCallback" Prop Callback that is executed when and item requests to be fixed/unfixed. * Add support to "addItem" method in the Action List control * Don't fire itemClick if the group's caption is not interactable * Show additional actions on item focus * Check if the item still exists before applying removeElement * Fix addItem implementation
1 parent 370d6b7 commit 9c13de6

File tree

2 files changed

+125
-10
lines changed

2 files changed

+125
-10
lines changed

src/components/action-list/action-list-render.tsx

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,6 @@ const defaultSortItemsCallback = (subModel: ActionListItemModel[]): void => {
166166
});
167167
};
168168

169-
// type ImmediateFilter = "immediate" | "debounced" | undefined;
170-
171169
@Component({
172170
tag: "ch-action-list-render",
173171
styleUrl: "action-list-render.scss",
@@ -206,6 +204,16 @@ export class ChActionListRender {
206204
*/
207205
@Prop() readonly editableItems: boolean = DEFAULT_EDITABLE_ITEMS_VALUE;
208206

207+
/**
208+
* Callback that is executed when and item requests to be fixed/unfixed.
209+
* If the callback is not defined, the item will be fixed/unfixed without
210+
* further confirmation.
211+
*/
212+
@Prop() readonly fixItemCallback?: (
213+
itemInfo: ActionListItemActionable,
214+
newFixedValue: boolean
215+
) => Promise<boolean>;
216+
209217
/**
210218
* This property lets you define the model of the control.
211219
*/
@@ -377,9 +385,66 @@ export class ChActionListRender {
377385

378386
/**
379387
* Fired when an item is clicked and `selection === "none"`.
388+
* Applies for items that have `type === "actionable"` or
389+
* (`type === "group"` and `expandable === true`)
380390
*/
381391
@Event() itemClick: EventEmitter<ActionListItemModelExtended>;
382392

393+
/**
394+
* Adds an item in the control.
395+
*
396+
* If the item already exists, the operation is canceled.
397+
*
398+
* If the `groupParentId` property is specified the item is added in the
399+
* group determined by `groupParentId`. It only works if the item to add
400+
* has `type === "actionable"`
401+
*/
402+
@Method()
403+
async addItem(
404+
itemInfo: ActionListItemModel,
405+
groupParentId?: string
406+
): Promise<void> {
407+
// Already exists
408+
if (this.#flattenedModel.get(itemInfo.id)) {
409+
return;
410+
}
411+
412+
if (groupParentId) {
413+
const parentGroup = this.#flattenedModel.get(groupParentId);
414+
415+
// The parent group does not exists or it isn't a group
416+
if (
417+
!parentGroup ||
418+
parentGroup.item.type !== "group" ||
419+
itemInfo.type !== "actionable"
420+
) {
421+
return;
422+
}
423+
424+
parentGroup.item.items.push(itemInfo);
425+
this.#flattenedModel.set(itemInfo.id, {
426+
item: itemInfo,
427+
parentItem: parentGroup.item
428+
});
429+
430+
// Sort items in parent model
431+
this.#sortModel(parentGroup.item.items);
432+
}
433+
// Item is placed at the root
434+
else {
435+
this.model.push(itemInfo);
436+
this.#flattenedModel.set(itemInfo.id, {
437+
item: itemInfo,
438+
root: this.model
439+
});
440+
441+
// Sort items in parent model
442+
this.#sortModel(this.model);
443+
}
444+
445+
forceUpdate(this);
446+
}
447+
383448
/**
384449
* Given a list of ids, it returns an array of the items that exists in the
385450
* given list.
@@ -391,6 +456,29 @@ export class ChActionListRender {
391456
return this.#getItemsInfo(itemsId);
392457
}
393458

459+
/**
460+
* Remove the item and all its descendants from the control.
461+
*/
462+
@Method()
463+
async removeItem(itemId: string) {
464+
const itemUIModel = this.#flattenedModel.get(itemId);
465+
466+
if (!itemUIModel) {
467+
return;
468+
}
469+
470+
// Remove all descendants
471+
if (itemUIModel.item.type === "group") {
472+
const items = itemUIModel.item.items;
473+
474+
items.forEach(item => {
475+
this.#flattenedModel.delete(item.id);
476+
});
477+
}
478+
479+
this.#removeItem(itemUIModel);
480+
}
481+
394482
#getItemsInfo = (itemsId: string[]): ActionListItemModelExtended[] => {
395483
const actionListItemsInfo: ActionListItemModelExtended[] = [];
396484

@@ -413,7 +501,25 @@ export class ChActionListRender {
413501

414502
const itemUIModel = this.#flattenedModel.get(detail.itemId);
415503
const itemInfo = itemUIModel.item as ActionListItemActionable;
416-
itemInfo.fixed = detail.value;
504+
505+
if (!this.fixItemCallback) {
506+
this.#updateItemFix(itemUIModel, itemInfo, detail.value);
507+
return;
508+
}
509+
510+
this.fixItemCallback(itemInfo, detail.value).then(acceptChange => {
511+
if (acceptChange) {
512+
this.#updateItemFix(itemUIModel, itemInfo, detail.value);
513+
}
514+
});
515+
}
516+
517+
#updateItemFix = (
518+
itemUIModel: ActionListItemModelExtended,
519+
itemInfo: ActionListItemActionable,
520+
newFixedValue: boolean
521+
) => {
522+
itemInfo.fixed = newFixedValue;
417523

418524
// Sort items in parent model
419525
this.#sortModel(
@@ -423,7 +529,7 @@ export class ChActionListRender {
423529

424530
// Queue a re-render to update the fixed binding and the order of the items
425531
forceUpdate(this);
426-
}
532+
};
427533

428534
@Listen("remove")
429535
onRemove(event: ChActionListItemCustomEvent<string>) {
@@ -491,6 +597,9 @@ export class ChActionListRender {
491597
const itemInfo = this.#getItemOrGroupInfo(actionListItemOrGroup.id);
492598
this.#checkIfMustExpandCollapseGroup(itemInfo);
493599

600+
if (itemInfo.type === "group" && !itemInfo.expandable) {
601+
return;
602+
}
494603
this.itemClick.emit(this.#flattenedModel.get(itemInfo.id));
495604
};
496605

@@ -594,15 +703,21 @@ export class ChActionListRender {
594703
const parentArray =
595704
(itemUIModel as ActionListItemModelExtendedRoot).root ??
596705
(itemUIModel as ActionListItemModelExtendedGroup).parentItem.items;
597-
const itemInfo = itemUIModel.item as ActionListItemActionable;
706+
const itemToRemoveId = itemUIModel.item.id;
598707

599708
const itemToRemoveIndex = parentArray.findIndex(
600-
el => (el as ActionListItemActionable).id === itemInfo.id
709+
el => el.id === itemToRemoveId
601710
);
602711

603-
// Remove the UI model from the previous parent. The equality function
604-
// must be by index, not by object reference
605-
removeElement(parentArray, itemToRemoveIndex);
712+
// In some situations, the user could remove the item before the
713+
// "removeItemCallback" promise is resolved
714+
if (itemToRemoveIndex > -1) {
715+
// Remove the UI model from the previous parent. The equality function
716+
// must be by index, not by object reference
717+
removeElement(parentArray, itemToRemoveIndex);
718+
}
719+
720+
this.#flattenedModel.delete(itemToRemoveId);
606721

607722
// Queue a re-render
608723
forceUpdate(this);

src/components/action-list/internal/action-list-item/action-list-item.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
// - - - - - - - - - - - - - - - - - - - -
8787
// Additional items
8888
// - - - - - - - - - - - - - - - - - - - -
89-
:host(:not(:hover)) .show-on-mouse-hover {
89+
:host(:not(:hover):not(:focus-within)) .show-on-mouse-hover {
9090
display: none;
9191
}
9292

0 commit comments

Comments
 (0)