@xui/transfer 2.0.0-alpha.20 → 2.0.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -16,16 +16,21 @@ import { createXValueAccessor, provideXValueAccessor } from '@xui/core/forms';
|
|
|
16
16
|
class XuiTransfer {
|
|
17
17
|
SIDES = ['left', 'right'];
|
|
18
18
|
host = inject(ElementRef);
|
|
19
|
+
/** Extra classes, merged into the component's own rather than replacing them. */
|
|
19
20
|
class = input('', /* @ts-ignore */
|
|
20
21
|
...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
22
|
+
/** Every item, on both sides. Which side each one is on comes from the bound value, not from this list. */
|
|
21
23
|
items = input([], /* @ts-ignore */
|
|
22
24
|
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
23
25
|
/** The target-list keys. Two-way bindable with `[(values)]`, or via `formControl`/`ngModel`. */
|
|
24
26
|
values = model([], /* @ts-ignore */
|
|
25
27
|
...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
|
|
28
|
+
/** Headings for the two lists, source first. */
|
|
26
29
|
titles = input(['Source', 'Target'], /* @ts-ignore */
|
|
27
30
|
...(ngDevMode ? [{ debugName: "titles" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Add a filter field above each list. */
|
|
28
32
|
searchable = input(false, { ...(ngDevMode ? { debugName: "searchable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
33
|
+
/** Block interaction and dim both lists. */
|
|
29
34
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
30
35
|
cva = createXValueAccessor({
|
|
31
36
|
onWrite: values => this.values.set(values ?? []),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xui-transfer.mjs","sources":["../../../../../../libs/ui/transfer/xui/src/lib/transfer.ts","../../../../../../libs/ui/transfer/xui/src/index.ts","../../../../../../libs/ui/transfer/xui/src/xui-transfer.ts"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n inject,\n input,\n model,\n signal,\n ViewEncapsulation\n} from '@angular/core';\nimport type { ControlValueAccessor } from '@angular/forms';\nimport { xui } from '@xui/core';\nimport { uniqueId } from '@xui/core/a11y';\nimport { createXValueAccessor, provideXValueAccessor } from '@xui/core/forms';\nimport type { ClassValue } from 'clsx';\n\nexport interface XuiTransferItem {\n key: string;\n title: string;\n disabled?: boolean;\n}\n\ntype Side = 'left' | 'right';\n\n/**\n * A dual list-box that moves items between a source and a target list. `items`\n * is the full set; `values` (two-way) are the keys on the right. Check items\n * and use the arrow buttons to move them; each side can be searched.\n *\n * ```html\n * <xui-transfer [items]=\"all\" [(values)]=\"selected\" searchable />\n * ```\n */\n@Component({\n selector: 'xui-transfer',\n template: `\n @for (side of SIDES; track side) {\n <div [class]=\"panelClass()\">\n <div class=\"border-border flex items-center gap-2 border-b px-3 py-2\">\n <button\n type=\"button\"\n role=\"checkbox\"\n [class]=\"checkboxClass(allChecked(side))\"\n [disabled]=\"isDisabled()\"\n [attr.aria-checked]=\"allChecked(side) ? 'true' : someChecked(side) ? 'mixed' : 'false'\"\n [attr.aria-label]=\"'Select all in ' + title(side)\"\n (click)=\"toggleAll(side)\"\n >\n @if (allChecked(side)) {\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3\" fill=\"none\">\n <path\n d=\"M20 6 9 17l-5-5\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else if (someChecked(side)) {\n <span class=\"bg-primary-foreground h-0.5 w-2\"></span>\n }\n </button>\n <span class=\"text-foreground text-sm font-medium\">{{ title(side) }}</span>\n <span class=\"text-foreground-muted ms-auto text-xs\">{{ checkedCount(side) }}/{{ list(side).length }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"border-border border-b p-2\">\n <input\n [class]=\"searchClass()\"\n [disabled]=\"isDisabled()\"\n [value]=\"side === 'left' ? searchLeft() : searchRight()\"\n (input)=\"setSearch(side, $any($event.target).value)\"\n [attr.placeholder]=\"'Search'\"\n aria-label=\"Search\"\n />\n </div>\n }\n\n <!-- A multi-select listbox: one tab stop per side, arrow keys move the\n active option, Space/Enter checks it. -->\n <ul\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"title(side)\"\n [attr.aria-activedescendant]=\"activeOptionId(side)\"\n class=\"min-h-40 flex-1 overflow-auto p-1\"\n [tabindex]=\"filtered(side).length && !isDisabled() ? 0 : -1\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (keydown)=\"onListKeydown(side, $event)\"\n >\n @for (item of filtered(side); track item.key) {\n <!-- The keyboard path is the listbox's, not the option's: under\n aria-activedescendant the container holds the tab stop and the\n key handler, so a per-option one would be a second, conflicting\n tab stop. The lint rules only look at this element. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(side, item.key)\"\n [class]=\"itemClass(item, activeKey(side) === item.key)\"\n [attr.aria-selected]=\"checked(side).has(item.key)\"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"activate(side, item)\"\n >\n <span [class]=\"checkboxClass(checked(side).has(item.key))\">\n @if (checked(side).has(item.key)) {\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3\" fill=\"none\">\n <path\n d=\"M20 6 9 17l-5-5\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </span>\n <span class=\"truncate\">{{ item.title }}</span>\n </li>\n } @empty {\n <li class=\"text-foreground-subtle px-2 py-6 text-center text-sm\">No items</li>\n }\n </ul>\n </div>\n\n @if (side === 'left') {\n <div class=\"flex flex-col justify-center gap-2 px-2\">\n <button\n type=\"button\"\n [class]=\"moveClass()\"\n [disabled]=\"isDisabled() || checkedCount('left') === 0\"\n (click)=\"move('right')\"\n aria-label=\"Move right\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\">\n <path\n d=\"M9 6l6 6-6 6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n [class]=\"moveClass()\"\n [disabled]=\"isDisabled() || checkedCount('right') === 0\"\n (click)=\"move('left')\"\n aria-label=\"Move left\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\">\n <path\n d=\"M15 6l-6 6 6 6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n }\n }\n `,\n host: {\n '[class]': 'computedClass()',\n '(focusout)': 'onFocusOut($event)'\n },\n providers: [provideXValueAccessor(() => XuiTransfer)],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class XuiTransfer implements ControlValueAccessor {\n protected readonly SIDES: Side[] = ['left', 'right'];\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n readonly class = input<ClassValue>('');\n readonly items = input<XuiTransferItem[]>([]);\n /** The target-list keys. Two-way bindable with `[(values)]`, or via `formControl`/`ngModel`. */\n readonly values = model<string[]>([]);\n readonly titles = input<[string, string]>(['Source', 'Target']);\n readonly searchable = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n protected readonly cva = createXValueAccessor<string[]>({\n onWrite: values => this.values.set(values ?? []),\n disabled: this.disabled\n });\n protected readonly isDisabled = this.cva.disabled;\n\n private readonly checkedLeft = signal(new Set<string>());\n private readonly checkedRight = signal(new Set<string>());\n protected readonly searchLeft = signal('');\n protected readonly searchRight = signal('');\n\n /** The option the arrow keys are on, per side — see `aria-activedescendant`. */\n private readonly activeLeft = signal<string | null>(null);\n private readonly activeRight = signal<string | null>(null);\n private readonly idPrefix = uniqueId('xui-transfer');\n\n private readonly targetSet = computed(() => new Set(this.values()));\n protected readonly leftItems = computed(() => this.items().filter(item => !this.targetSet().has(item.key)));\n protected readonly rightItems = computed(() => this.items().filter(item => this.targetSet().has(item.key)));\n\n protected title(side: Side): string {\n return side === 'left' ? this.titles()[0] : this.titles()[1];\n }\n protected list(side: Side): XuiTransferItem[] {\n return side === 'left' ? this.leftItems() : this.rightItems();\n }\n protected checked(side: Side): Set<string> {\n return side === 'left' ? this.checkedLeft() : this.checkedRight();\n }\n\n protected filtered(side: Side): XuiTransferItem[] {\n const query = (side === 'left' ? this.searchLeft() : this.searchRight()).toLowerCase().trim();\n const list = this.list(side);\n return query ? list.filter(item => item.title.toLowerCase().includes(query)) : list;\n }\n\n protected checkedCount(side: Side): number {\n const checked = this.checked(side);\n return this.list(side).filter(item => checked.has(item.key)).length;\n }\n protected allChecked(side: Side): boolean {\n const enabled = this.filtered(side).filter(item => !item.disabled);\n return enabled.length > 0 && enabled.every(item => this.checked(side).has(item.key));\n }\n protected someChecked(side: Side): boolean {\n return this.checkedCount(side) > 0 && !this.allChecked(side);\n }\n\n protected setSearch(side: Side, value: string): void {\n (side === 'left' ? this.searchLeft : this.searchRight).set(value);\n // The old active option may have been filtered away.\n (side === 'left' ? this.activeLeft : this.activeRight).set(null);\n }\n\n // --- keyboard -------------------------------------------------------------\n\n protected optionId(side: Side, key: string): string {\n return `${this.idPrefix}-${side}-${key}`;\n }\n\n /** The active option's key, defaulting to the first one so a fresh Tab lands somewhere. */\n protected activeKey(side: Side): string | null {\n const active = side === 'left' ? this.activeLeft() : this.activeRight();\n const options = this.filtered(side);\n if (active && options.some(item => item.key === active)) {\n return active;\n }\n return options[0]?.key ?? null;\n }\n\n protected activeOptionId(side: Side): string | null {\n const key = this.activeKey(side);\n return key === null ? null : this.optionId(side, key);\n }\n\n /** Move the active option and check it — clicking does both, so keys match. */\n protected activate(side: Side, item: XuiTransferItem): void {\n if (this.isDisabled()) {\n return;\n }\n (side === 'left' ? this.activeLeft : this.activeRight).set(item.key);\n this.toggle(side, item);\n }\n\n protected onListKeydown(side: Side, event: KeyboardEvent): void {\n const options = this.filtered(side);\n if (!options.length || this.isDisabled()) {\n return;\n }\n\n const current = Math.max(\n 0,\n options.findIndex(item => item.key === this.activeKey(side))\n );\n let next: number;\n\n switch (event.key) {\n case 'ArrowDown':\n next = Math.min(options.length - 1, current + 1);\n break;\n case 'ArrowUp':\n next = Math.max(0, current - 1);\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = options.length - 1;\n break;\n case ' ':\n case 'Enter':\n event.preventDefault();\n this.toggle(side, options[current]);\n return;\n default:\n return;\n }\n\n event.preventDefault();\n (side === 'left' ? this.activeLeft : this.activeRight).set(options[next].key);\n }\n\n protected toggle(side: Side, item: XuiTransferItem): void {\n if (item.disabled || this.isDisabled()) {\n return;\n }\n const signalRef = side === 'left' ? this.checkedLeft : this.checkedRight;\n const next = new Set(signalRef());\n if (next.has(item.key)) {\n next.delete(item.key);\n } else {\n next.add(item.key);\n }\n signalRef.set(next);\n }\n\n protected toggleAll(side: Side): void {\n if (this.isDisabled()) {\n return;\n }\n const signalRef = side === 'left' ? this.checkedLeft : this.checkedRight;\n const enabled = this.filtered(side).filter(item => !item.disabled);\n if (this.allChecked(side)) {\n const next = new Set(signalRef());\n enabled.forEach(item => next.delete(item.key));\n signalRef.set(next);\n } else {\n const next = new Set(signalRef());\n enabled.forEach(item => next.add(item.key));\n signalRef.set(next);\n }\n }\n\n protected move(to: Side): void {\n if (this.isDisabled()) {\n return;\n }\n if (to === 'right') {\n const moving = this.leftItems().filter(item => !item.disabled && this.checkedLeft().has(item.key));\n this.values.set([...this.values(), ...moving.map(item => item.key)]);\n this.checkedLeft.set(new Set());\n } else {\n const movingKeys = new Set(\n this.rightItems()\n .filter(item => !item.disabled && this.checkedRight().has(item.key))\n .map(item => item.key)\n );\n this.values.set(this.values().filter(key => !movingKeys.has(key)));\n this.checkedRight.set(new Set());\n }\n this.cva.notifyChange(this.values());\n }\n\n /** Touched fires only when focus leaves the whole widget, not between its panes. */\n protected onFocusOut(event: FocusEvent): void {\n if (!this.host.nativeElement.contains(event.relatedTarget as Node | null)) {\n this.cva.markTouched();\n }\n }\n\n protected readonly computedClass = computed(() =>\n xui('flex items-stretch', this.isDisabled() && 'opacity-50', this.class())\n );\n protected readonly panelClass = computed(() => xui('border-border bg-surface flex w-56 flex-col rounded-lg border'));\n protected readonly searchClass = computed(() =>\n xui(\n 'border-border bg-surface text-foreground focus-visible:border-focus h-(--control-height-md) w-full rounded-md border px-(--control-padding-sm) text-sm focus-visible:outline-none'\n )\n );\n\n protected checkboxClass(active: boolean): string {\n return xui(\n 'flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded border',\n active ? 'bg-primary border-primary text-primary-foreground' : 'border-border'\n );\n }\n protected itemClass(item: XuiTransferItem, active: boolean): string {\n return xui(\n 'flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-sm select-none',\n item.disabled ? 'text-foreground-subtle cursor-not-allowed' : 'hover:bg-surface-inset',\n // The list owns the tab stop, so the active option is marked here rather\n // than by a focus ring on the option itself.\n active && 'bg-surface-inset'\n );\n }\n // Disabled, the button keeps its pointer events so the cursor can say it is unavailable — an\n // element that receives none contributes no cursor either. The native `disabled` swallows the\n // click; `enabled:` keeps the hover lift off a button that cannot be pressed.\n protected moveClass(): string {\n return xui(\n 'border-border text-foreground enabled:hover:bg-surface-inset flex size-(--control-height-md) items-center justify-center rounded-md border disabled:cursor-not-allowed disabled:opacity-40'\n );\n }\n\n readonly writeValue = this.cva.writeValue;\n readonly registerOnChange = this.cva.registerOnChange;\n readonly registerOnTouched = this.cva.registerOnTouched;\n readonly setDisabledState = this.cva.setDisabledState;\n}\n","import { XuiTransfer } from './lib/transfer';\n\nexport * from './lib/transfer';\n\nexport const XuiTransferImports = [XuiTransfer] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"xui-transfer.mjs","sources":["../../../../../../libs/ui/transfer/xui/src/lib/transfer.ts","../../../../../../libs/ui/transfer/xui/src/index.ts","../../../../../../libs/ui/transfer/xui/src/xui-transfer.ts"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n inject,\n input,\n model,\n signal,\n ViewEncapsulation\n} from '@angular/core';\nimport type { ControlValueAccessor } from '@angular/forms';\nimport { xui } from '@xui/core';\nimport { uniqueId } from '@xui/core/a11y';\nimport { createXValueAccessor, provideXValueAccessor } from '@xui/core/forms';\nimport type { ClassValue } from 'clsx';\n\nexport interface XuiTransferItem {\n key: string;\n title: string;\n disabled?: boolean;\n}\n\ntype Side = 'left' | 'right';\n\n/**\n * A dual list-box that moves items between a source and a target list. `items`\n * is the full set; `values` (two-way) are the keys on the right. Check items\n * and use the arrow buttons to move them; each side can be searched.\n *\n * ```html\n * <xui-transfer [items]=\"all\" [(values)]=\"selected\" searchable />\n * ```\n */\n@Component({\n selector: 'xui-transfer',\n template: `\n @for (side of SIDES; track side) {\n <div [class]=\"panelClass()\">\n <div class=\"border-border flex items-center gap-2 border-b px-3 py-2\">\n <button\n type=\"button\"\n role=\"checkbox\"\n [class]=\"checkboxClass(allChecked(side))\"\n [disabled]=\"isDisabled()\"\n [attr.aria-checked]=\"allChecked(side) ? 'true' : someChecked(side) ? 'mixed' : 'false'\"\n [attr.aria-label]=\"'Select all in ' + title(side)\"\n (click)=\"toggleAll(side)\"\n >\n @if (allChecked(side)) {\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3\" fill=\"none\">\n <path\n d=\"M20 6 9 17l-5-5\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else if (someChecked(side)) {\n <span class=\"bg-primary-foreground h-0.5 w-2\"></span>\n }\n </button>\n <span class=\"text-foreground text-sm font-medium\">{{ title(side) }}</span>\n <span class=\"text-foreground-muted ms-auto text-xs\">{{ checkedCount(side) }}/{{ list(side).length }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"border-border border-b p-2\">\n <input\n [class]=\"searchClass()\"\n [disabled]=\"isDisabled()\"\n [value]=\"side === 'left' ? searchLeft() : searchRight()\"\n (input)=\"setSearch(side, $any($event.target).value)\"\n [attr.placeholder]=\"'Search'\"\n aria-label=\"Search\"\n />\n </div>\n }\n\n <!-- A multi-select listbox: one tab stop per side, arrow keys move the\n active option, Space/Enter checks it. -->\n <ul\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"title(side)\"\n [attr.aria-activedescendant]=\"activeOptionId(side)\"\n class=\"min-h-40 flex-1 overflow-auto p-1\"\n [tabindex]=\"filtered(side).length && !isDisabled() ? 0 : -1\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (keydown)=\"onListKeydown(side, $event)\"\n >\n @for (item of filtered(side); track item.key) {\n <!-- The keyboard path is the listbox's, not the option's: under\n aria-activedescendant the container holds the tab stop and the\n key handler, so a per-option one would be a second, conflicting\n tab stop. The lint rules only look at this element. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(side, item.key)\"\n [class]=\"itemClass(item, activeKey(side) === item.key)\"\n [attr.aria-selected]=\"checked(side).has(item.key)\"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"activate(side, item)\"\n >\n <span [class]=\"checkboxClass(checked(side).has(item.key))\">\n @if (checked(side).has(item.key)) {\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3\" fill=\"none\">\n <path\n d=\"M20 6 9 17l-5-5\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </span>\n <span class=\"truncate\">{{ item.title }}</span>\n </li>\n } @empty {\n <li class=\"text-foreground-subtle px-2 py-6 text-center text-sm\">No items</li>\n }\n </ul>\n </div>\n\n @if (side === 'left') {\n <div class=\"flex flex-col justify-center gap-2 px-2\">\n <button\n type=\"button\"\n [class]=\"moveClass()\"\n [disabled]=\"isDisabled() || checkedCount('left') === 0\"\n (click)=\"move('right')\"\n aria-label=\"Move right\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\">\n <path\n d=\"M9 6l6 6-6 6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n [class]=\"moveClass()\"\n [disabled]=\"isDisabled() || checkedCount('right') === 0\"\n (click)=\"move('left')\"\n aria-label=\"Move left\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\">\n <path\n d=\"M15 6l-6 6 6 6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n }\n }\n `,\n host: {\n '[class]': 'computedClass()',\n '(focusout)': 'onFocusOut($event)'\n },\n providers: [provideXValueAccessor(() => XuiTransfer)],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class XuiTransfer implements ControlValueAccessor {\n protected readonly SIDES: Side[] = ['left', 'right'];\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /** Extra classes, merged into the component's own rather than replacing them. */\n readonly class = input<ClassValue>('');\n /** Every item, on both sides. Which side each one is on comes from the bound value, not from this list. */\n readonly items = input<XuiTransferItem[]>([]);\n /** The target-list keys. Two-way bindable with `[(values)]`, or via `formControl`/`ngModel`. */\n readonly values = model<string[]>([]);\n /** Headings for the two lists, source first. */\n readonly titles = input<[string, string]>(['Source', 'Target']);\n /** Add a filter field above each list. */\n readonly searchable = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n /** Block interaction and dim both lists. */\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n protected readonly cva = createXValueAccessor<string[]>({\n onWrite: values => this.values.set(values ?? []),\n disabled: this.disabled\n });\n protected readonly isDisabled = this.cva.disabled;\n\n private readonly checkedLeft = signal(new Set<string>());\n private readonly checkedRight = signal(new Set<string>());\n protected readonly searchLeft = signal('');\n protected readonly searchRight = signal('');\n\n /** The option the arrow keys are on, per side — see `aria-activedescendant`. */\n private readonly activeLeft = signal<string | null>(null);\n private readonly activeRight = signal<string | null>(null);\n private readonly idPrefix = uniqueId('xui-transfer');\n\n private readonly targetSet = computed(() => new Set(this.values()));\n protected readonly leftItems = computed(() => this.items().filter(item => !this.targetSet().has(item.key)));\n protected readonly rightItems = computed(() => this.items().filter(item => this.targetSet().has(item.key)));\n\n protected title(side: Side): string {\n return side === 'left' ? this.titles()[0] : this.titles()[1];\n }\n protected list(side: Side): XuiTransferItem[] {\n return side === 'left' ? this.leftItems() : this.rightItems();\n }\n protected checked(side: Side): Set<string> {\n return side === 'left' ? this.checkedLeft() : this.checkedRight();\n }\n\n protected filtered(side: Side): XuiTransferItem[] {\n const query = (side === 'left' ? this.searchLeft() : this.searchRight()).toLowerCase().trim();\n const list = this.list(side);\n return query ? list.filter(item => item.title.toLowerCase().includes(query)) : list;\n }\n\n protected checkedCount(side: Side): number {\n const checked = this.checked(side);\n return this.list(side).filter(item => checked.has(item.key)).length;\n }\n protected allChecked(side: Side): boolean {\n const enabled = this.filtered(side).filter(item => !item.disabled);\n return enabled.length > 0 && enabled.every(item => this.checked(side).has(item.key));\n }\n protected someChecked(side: Side): boolean {\n return this.checkedCount(side) > 0 && !this.allChecked(side);\n }\n\n protected setSearch(side: Side, value: string): void {\n (side === 'left' ? this.searchLeft : this.searchRight).set(value);\n // The old active option may have been filtered away.\n (side === 'left' ? this.activeLeft : this.activeRight).set(null);\n }\n\n // --- keyboard -------------------------------------------------------------\n\n protected optionId(side: Side, key: string): string {\n return `${this.idPrefix}-${side}-${key}`;\n }\n\n /** The active option's key, defaulting to the first one so a fresh Tab lands somewhere. */\n protected activeKey(side: Side): string | null {\n const active = side === 'left' ? this.activeLeft() : this.activeRight();\n const options = this.filtered(side);\n if (active && options.some(item => item.key === active)) {\n return active;\n }\n return options[0]?.key ?? null;\n }\n\n protected activeOptionId(side: Side): string | null {\n const key = this.activeKey(side);\n return key === null ? null : this.optionId(side, key);\n }\n\n /** Move the active option and check it — clicking does both, so keys match. */\n protected activate(side: Side, item: XuiTransferItem): void {\n if (this.isDisabled()) {\n return;\n }\n (side === 'left' ? this.activeLeft : this.activeRight).set(item.key);\n this.toggle(side, item);\n }\n\n protected onListKeydown(side: Side, event: KeyboardEvent): void {\n const options = this.filtered(side);\n if (!options.length || this.isDisabled()) {\n return;\n }\n\n const current = Math.max(\n 0,\n options.findIndex(item => item.key === this.activeKey(side))\n );\n let next: number;\n\n switch (event.key) {\n case 'ArrowDown':\n next = Math.min(options.length - 1, current + 1);\n break;\n case 'ArrowUp':\n next = Math.max(0, current - 1);\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = options.length - 1;\n break;\n case ' ':\n case 'Enter':\n event.preventDefault();\n this.toggle(side, options[current]);\n return;\n default:\n return;\n }\n\n event.preventDefault();\n (side === 'left' ? this.activeLeft : this.activeRight).set(options[next].key);\n }\n\n protected toggle(side: Side, item: XuiTransferItem): void {\n if (item.disabled || this.isDisabled()) {\n return;\n }\n const signalRef = side === 'left' ? this.checkedLeft : this.checkedRight;\n const next = new Set(signalRef());\n if (next.has(item.key)) {\n next.delete(item.key);\n } else {\n next.add(item.key);\n }\n signalRef.set(next);\n }\n\n protected toggleAll(side: Side): void {\n if (this.isDisabled()) {\n return;\n }\n const signalRef = side === 'left' ? this.checkedLeft : this.checkedRight;\n const enabled = this.filtered(side).filter(item => !item.disabled);\n if (this.allChecked(side)) {\n const next = new Set(signalRef());\n enabled.forEach(item => next.delete(item.key));\n signalRef.set(next);\n } else {\n const next = new Set(signalRef());\n enabled.forEach(item => next.add(item.key));\n signalRef.set(next);\n }\n }\n\n protected move(to: Side): void {\n if (this.isDisabled()) {\n return;\n }\n if (to === 'right') {\n const moving = this.leftItems().filter(item => !item.disabled && this.checkedLeft().has(item.key));\n this.values.set([...this.values(), ...moving.map(item => item.key)]);\n this.checkedLeft.set(new Set());\n } else {\n const movingKeys = new Set(\n this.rightItems()\n .filter(item => !item.disabled && this.checkedRight().has(item.key))\n .map(item => item.key)\n );\n this.values.set(this.values().filter(key => !movingKeys.has(key)));\n this.checkedRight.set(new Set());\n }\n this.cva.notifyChange(this.values());\n }\n\n /** Touched fires only when focus leaves the whole widget, not between its panes. */\n protected onFocusOut(event: FocusEvent): void {\n if (!this.host.nativeElement.contains(event.relatedTarget as Node | null)) {\n this.cva.markTouched();\n }\n }\n\n protected readonly computedClass = computed(() =>\n xui('flex items-stretch', this.isDisabled() && 'opacity-50', this.class())\n );\n protected readonly panelClass = computed(() => xui('border-border bg-surface flex w-56 flex-col rounded-lg border'));\n protected readonly searchClass = computed(() =>\n xui(\n 'border-border bg-surface text-foreground focus-visible:border-focus h-(--control-height-md) w-full rounded-md border px-(--control-padding-sm) text-sm focus-visible:outline-none'\n )\n );\n\n protected checkboxClass(active: boolean): string {\n return xui(\n 'flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded border',\n active ? 'bg-primary border-primary text-primary-foreground' : 'border-border'\n );\n }\n protected itemClass(item: XuiTransferItem, active: boolean): string {\n return xui(\n 'flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-sm select-none',\n item.disabled ? 'text-foreground-subtle cursor-not-allowed' : 'hover:bg-surface-inset',\n // The list owns the tab stop, so the active option is marked here rather\n // than by a focus ring on the option itself.\n active && 'bg-surface-inset'\n );\n }\n // Disabled, the button keeps its pointer events so the cursor can say it is unavailable — an\n // element that receives none contributes no cursor either. The native `disabled` swallows the\n // click; `enabled:` keeps the hover lift off a button that cannot be pressed.\n protected moveClass(): string {\n return xui(\n 'border-border text-foreground enabled:hover:bg-surface-inset flex size-(--control-height-md) items-center justify-center rounded-md border disabled:cursor-not-allowed disabled:opacity-40'\n );\n }\n\n readonly writeValue = this.cva.writeValue;\n readonly registerOnChange = this.cva.registerOnChange;\n readonly registerOnTouched = this.cva.registerOnTouched;\n readonly setDisabledState = this.cva.setDisabledState;\n}\n","import { XuiTransfer } from './lib/transfer';\n\nexport * from './lib/transfer';\n\nexport const XuiTransferImports = [XuiTransfer] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xui/transfer",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.22",
|
|
4
4
|
"description": "Modern Angular 22 UI Library based on TailwindCSS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@angular/cdk": "22",
|
|
40
40
|
"@angular/core": "22",
|
|
41
41
|
"@angular/forms": "22",
|
|
42
|
-
"@xui/core": "2.0.0-alpha.
|
|
42
|
+
"@xui/core": "2.0.0-alpha.22",
|
|
43
43
|
"clsx": "^2.1.1"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
package/types/xui-transfer.d.ts
CHANGED
|
@@ -22,12 +22,17 @@ type Side = 'left' | 'right';
|
|
|
22
22
|
declare class XuiTransfer implements ControlValueAccessor {
|
|
23
23
|
protected readonly SIDES: Side[];
|
|
24
24
|
private readonly host;
|
|
25
|
+
/** Extra classes, merged into the component's own rather than replacing them. */
|
|
25
26
|
readonly class: _angular_core.InputSignal<ClassValue>;
|
|
27
|
+
/** Every item, on both sides. Which side each one is on comes from the bound value, not from this list. */
|
|
26
28
|
readonly items: _angular_core.InputSignal<XuiTransferItem[]>;
|
|
27
29
|
/** The target-list keys. Two-way bindable with `[(values)]`, or via `formControl`/`ngModel`. */
|
|
28
30
|
readonly values: _angular_core.ModelSignal<string[]>;
|
|
31
|
+
/** Headings for the two lists, source first. */
|
|
29
32
|
readonly titles: _angular_core.InputSignal<[string, string]>;
|
|
33
|
+
/** Add a filter field above each list. */
|
|
30
34
|
readonly searchable: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
|
|
35
|
+
/** Block interaction and dim both lists. */
|
|
31
36
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
|
|
32
37
|
protected readonly cva: _xui_core_forms.XValueAccessor<string[]>;
|
|
33
38
|
protected readonly isDisabled: _angular_core.Signal<boolean>;
|