Skip to content
Open
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
51 changes: 50 additions & 1 deletion packages/components-dev/list/module.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { Clipboard } from '@angular/cdk/clipboard';
import { moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { AsyncPipe, JsonPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, signal, ViewEncapsulation } from '@angular/core';
import { FormsModule, UntypedFormControl } from '@angular/forms';
import { PopUpPlacements } from '@koobiq/components/core';
import { KbqDropdownModule } from '@koobiq/components/dropdown';
import { KbqIconModule } from '@koobiq/components/icon';
import { KbqListModule, KbqListSelectionChange } from '@koobiq/components/list';
import { KbqListModule, KbqListSelectionChange, KbqListSelectionDroppedEvent } from '@koobiq/components/list';
import { KbqTitleModule } from '@koobiq/components/title';
import { KbqToolTipModule } from '@koobiq/components/tooltip';
import { ListExamplesModule } from 'packages/docs-examples/components/list';
import { of } from 'rxjs';
import { debounceTime, startWith, switchMap } from 'rxjs/operators';
import { DevThemeToggle } from '../theme-toggle';

type DevItem = { id: number; label: string };

const devItems = (prefix: string, offset: number, length: number): DevItem[] =>
Array.from({ length }, (_, i) => ({ id: offset + i, label: `${prefix} #${offset + i}` }));

@Component({
selector: 'dev-examples',
imports: [ListExamplesModule],
Expand All @@ -28,6 +34,12 @@ import { DevThemeToggle } from '../theme-toggle';
<br />
<br />
<list-multiple-checkbox-example />
<br />
<br />
<list-draggable-example />
<br />
<br />
<list-draggable-connected-example />
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
Expand Down Expand Up @@ -58,6 +70,12 @@ export class DevApp {

list = signal(Array.from({ length: 5 }, (_, i) => `Item ${i}`));

readonly draggableItems = signal(devItems('Task', 0, 5));
draggableSelected: DevItem[] = [];

readonly availableItems = signal(devItems('Available', 0, 4));
readonly chosenItems = signal(devItems('Chosen', 10, 3));

readonly options = Array.from({ length: 10000 }).map((_, i) => ({
id: i,
label: `Option #${i}`
Expand Down Expand Up @@ -97,4 +115,35 @@ export class DevApp {
onRemove(item: string) {
this.list.update((list) => list.filter((listItem) => listItem !== item));
}

onDropped({ previousIndex, currentIndex }: KbqListSelectionDroppedEvent) {
const items = [...this.draggableItems()];

moveItemInArray(items, previousIndex, currentIndex);

this.draggableItems.set(items);
}

onTransferred({ previousIndex, currentIndex, previousContainer, container, option }: KbqListSelectionDroppedEvent) {
const fromAvailable = this.availableItems().includes(option.value);
const source = fromAvailable ? this.availableItems : this.chosenItems;

if (previousContainer === container) {
const items = [...source()];

moveItemInArray(items, previousIndex, currentIndex);
source.set(items);

return;
}

const target = fromAvailable ? this.chosenItems : this.availableItems;
const from = [...source()];
const to = [...target()];

transferArrayItem(from, to, previousIndex, currentIndex);

source.set(from);
target.set(to);
}
}
42 changes: 42 additions & 0 deletions packages/components-dev/list/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@
<dev-examples />
<hr />

<h5>draggable — reorder within one list</h5>
<kbq-list-selection
multiple="checkbox"
[draggable]="true"
[(ngModel)]="draggableSelected"
(dropped)="onDropped($event)"
>
@for (item of draggableItems(); track item.id) {
<kbq-list-option [value]="item">{{ item.label }}</kbq-list-option>
}
</kbq-list-selection>
<div>order: {{ draggableItems() | json }}</div>

<h5>draggable — transfer between connected lists</h5>
<div style="display: flex; gap: 16px">
<kbq-list-selection
#available="kbqListSelection"
style="flex: 1"
multiple="checkbox"
[connectedTo]="[chosen]"
[draggable]="true"
(dropped)="onTransferred($event)"
>
@for (item of availableItems(); track item.id) {
<kbq-list-option [value]="item">{{ item.label }}</kbq-list-option>
}
</kbq-list-selection>
<kbq-list-selection
#chosen="kbqListSelection"
style="flex: 1"
multiple="checkbox"
[connectedTo]="[available]"
[draggable]="true"
(dropped)="onTransferred($event)"
>
@for (item of chosenItems(); track item.id) {
<kbq-list-option [value]="item">{{ item.label }}</kbq-list-option>
}
</kbq-list-selection>
</div>
<hr />

<h5>single selection</h5>
<kbq-list-selection [(ngModel)]="selected" (onCopy)="onCopy($event)">
<kbq-list-option [disabled]="true" [value]="'Disabled'">
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const enUSLocaleData = {
nextMonth: 'Next month',
clear: 'Clear',
showPassword: 'Show password',
hidePassword: 'Hide password'
hidePassword: 'Hide password',
listOptionMoved: '{{ label }}, position {{ index }} of {{ total }}'
} satisfies KbqA11yLocaleConfiguration,
select: { hiddenItemsText: '+{{ number }}', selectAll: 'Select all' } satisfies KbqSelectLocaleConfiguration,
datepicker: {
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/es-LA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const esLALocaleData = {
nextMonth: 'Mes siguiente',
clear: 'Borrar',
showPassword: 'Mostrar la contraseña',
hidePassword: 'Ocultar la contraseña'
hidePassword: 'Ocultar la contraseña',
listOptionMoved: '{{ label }}, posición {{ index }} de {{ total }}'
} satisfies KbqA11yLocaleConfiguration,
select: {
hiddenItemsText: '+{{ number }}',
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const ptBRLocaleData = {
nextMonth: 'Próximo mês',
clear: 'Apagar',
showPassword: 'Mostrar a senha',
hidePassword: 'Ocultar a senha'
hidePassword: 'Ocultar a senha',
listOptionMoved: '{{ label }}, posição {{ index }} de {{ total }}'
} satisfies KbqA11yLocaleConfiguration,
select: {
hiddenItemsText: '+{{ number }}',
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/ru-RU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const ruRULocaleData = {
nextMonth: 'Следующий месяц',
clear: 'Очистить',
showPassword: 'Показать пароль',
hidePassword: 'Скрыть пароль'
hidePassword: 'Скрыть пароль',
listOptionMoved: '{{ label }}, позиция {{ index }} из {{ total }}'
} satisfies KbqA11yLocaleConfiguration,
select: { hiddenItemsText: '+{{ number }}', selectAll: 'Выбрать все' } satisfies KbqSelectLocaleConfiguration,
datepicker: {
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/tk-TM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const tkTMLocaleData = {
nextMonth: 'Indiki aý',
clear: 'Arassala',
showPassword: 'Paroly görkez',
hidePassword: 'Paroly gizle'
hidePassword: 'Paroly gizle',
listOptionMoved: '{{ label }}, {{ total }} ýerden {{ index }}-nji ýer'
} satisfies KbqA11yLocaleConfiguration,
select: {
hiddenItemsText: '+{{ number }}',
Expand Down
10 changes: 8 additions & 2 deletions packages/components/core/locales/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { FormatterDurationTemplate } from '@koobiq/date-formatter';

/**
* Accessible names for the icon-only buttons the library renders itself.
* Text the library exposes to assistive tech only: accessible names for the icon-only buttons it
* renders itself, and live-region announcements.
*
* An icon carries no text, so without one of these a button has no accessible name at all (AXE
* `button-name`). They are announced by assistive tech and are never displayed.
* `button-name`). None of these strings are ever displayed.
*/
export type KbqA11yLocaleConfiguration = {
/** Close button of a modal, popover, sidepanel, content panel or notification center. */
Expand All @@ -29,6 +30,11 @@ export type KbqA11yLocaleConfiguration = {
showPassword: string;
/** Password form field button that masks the password. */
hidePassword: string;
/**
* Announced after an option of a draggable `kbq-list-selection` has been reordered.
* Supports the `{{ label }}`, `{{ index }}` and `{{ total }}` placeholders.
*/
listOptionMoved: string;
};

/** Locale configuration for `KbqCodeBlockModule`. */
Expand Down
Loading