@kouji-ui/core 0.6.1 → 0.6.3
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.
|
@@ -2438,10 +2438,22 @@ class KjListNavigator {
|
|
|
2438
2438
|
this.moveBy(-this.kjPageSize());
|
|
2439
2439
|
return;
|
|
2440
2440
|
case 'Enter':
|
|
2441
|
-
case ' ':
|
|
2442
2441
|
// Only preventDefault when we actually activate. Lets consumers
|
|
2443
|
-
// (e.g. combobox free-text Enter) fall through when nothing is
|
|
2444
|
-
|
|
2442
|
+
// (e.g. combobox free-text Enter) fall through when nothing is active.
|
|
2443
|
+
if (this.activeItem()) {
|
|
2444
|
+
e.preventDefault();
|
|
2445
|
+
this.activateCurrent();
|
|
2446
|
+
}
|
|
2447
|
+
return;
|
|
2448
|
+
case ' ':
|
|
2449
|
+
// Space activates a LIST — but in a text field it is a character the
|
|
2450
|
+
// user is typing, and the field owns it. A command palette highlights
|
|
2451
|
+
// a row for every query, so treating Space as activation there made
|
|
2452
|
+
// multi-word queries impossible: the space ran the highlighted command
|
|
2453
|
+
// instead of reaching the input. Enter remains the activation key from
|
|
2454
|
+
// a text field.
|
|
2455
|
+
if (isTextEntry(e.target))
|
|
2456
|
+
return;
|
|
2445
2457
|
if (this.activeItem()) {
|
|
2446
2458
|
e.preventDefault();
|
|
2447
2459
|
this.activateCurrent();
|
|
@@ -2488,6 +2500,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
2488
2500
|
},
|
|
2489
2501
|
}]
|
|
2490
2502
|
}], ctorParameters: () => [], propDecorators: { kjOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOrientation", required: false }] }], kjWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjWrap", required: false }] }], kjPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjPageSize", required: false }] }], kjActivateOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjActivateOnHover", required: false }] }], kjFocusMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjFocusMode", required: false }] }], kjActiveChange: [{ type: i0.Output, args: ["kjActiveChange"] }] } });
|
|
2503
|
+
/** Types of `<input>` that hold text the user types (Space is a character). */
|
|
2504
|
+
const NON_TEXT_INPUT_TYPES = new Set([
|
|
2505
|
+
'button',
|
|
2506
|
+
'checkbox',
|
|
2507
|
+
'color',
|
|
2508
|
+
'file',
|
|
2509
|
+
'image',
|
|
2510
|
+
'radio',
|
|
2511
|
+
'range',
|
|
2512
|
+
'reset',
|
|
2513
|
+
'submit',
|
|
2514
|
+
]);
|
|
2515
|
+
/** Whether a key event landed in a field where Space types a character. */
|
|
2516
|
+
function isTextEntry(target) {
|
|
2517
|
+
const el = target;
|
|
2518
|
+
if (!el || typeof el.tagName !== 'string')
|
|
2519
|
+
return false;
|
|
2520
|
+
if (el.isContentEditable)
|
|
2521
|
+
return true;
|
|
2522
|
+
if (el.tagName === 'TEXTAREA')
|
|
2523
|
+
return true;
|
|
2524
|
+
if (el.tagName !== 'INPUT')
|
|
2525
|
+
return false;
|
|
2526
|
+
const type = el.type?.toLowerCase() ?? 'text';
|
|
2527
|
+
return !NON_TEXT_INPUT_TYPES.has(type);
|
|
2528
|
+
}
|
|
2491
2529
|
|
|
2492
2530
|
// packages/core/src/primitives/list/selection.ts
|
|
2493
2531
|
/**
|
|
@@ -13522,8 +13560,23 @@ function nextConfirmPopupMessageId() {
|
|
|
13522
13560
|
* @doc-category Core/Overlay
|
|
13523
13561
|
*/
|
|
13524
13562
|
class KjConfirmPopup {
|
|
13525
|
-
/** The overlay controller
|
|
13526
|
-
|
|
13563
|
+
/** The overlay controller, when the trigger sits on this very element. */
|
|
13564
|
+
selfController = inject(KjOverlayController, { optional: true });
|
|
13565
|
+
/**
|
|
13566
|
+
* The controller of a nested `[kjConfirmPopupTrigger]`. The documented
|
|
13567
|
+
* composition puts the trigger on a CHILD of `[kjConfirmPopup]` (see every
|
|
13568
|
+
* example), so the controller is NOT in this element's injector — without
|
|
13569
|
+
* this registration `close()` bailed out and `(kjConfirmed)` never fired.
|
|
13570
|
+
*/
|
|
13571
|
+
registered = signal(null, /* @ts-ignore */
|
|
13572
|
+
...(ngDevMode ? [{ debugName: "registered" }] : /* istanbul ignore next */ []));
|
|
13573
|
+
/** @internal — called by a nested `[kjConfirmPopupTrigger]`. */
|
|
13574
|
+
_registerController(c) {
|
|
13575
|
+
this.registered.set(c);
|
|
13576
|
+
}
|
|
13577
|
+
controllerOf() {
|
|
13578
|
+
return this.selfController ?? this.registered();
|
|
13579
|
+
}
|
|
13527
13580
|
/** When `true`, the confirm action renders with destructive intent. */
|
|
13528
13581
|
kjDestructive = input(false, { ...(ngDevMode ? { debugName: "kjDestructive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
13529
13582
|
/** Where to place initial focus. Default `'cancel'`. */
|
|
@@ -13537,10 +13590,8 @@ class KjConfirmPopup {
|
|
|
13537
13590
|
kjResult = output();
|
|
13538
13591
|
// ── KjConfirmPopupContext ──────────────────────────────────────────
|
|
13539
13592
|
/** Open state forwarded from the underlying controller. */
|
|
13540
|
-
open = (() =>
|
|
13541
|
-
|
|
13542
|
-
return ctrl ? ctrl.isOpen : (() => false);
|
|
13543
|
-
})();
|
|
13593
|
+
open = computed(() => this.controllerOf()?.isOpen() ?? false, /* @ts-ignore */
|
|
13594
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
13544
13595
|
/** Whether the confirm action should render destructively. */
|
|
13545
13596
|
destructive = this.kjDestructive;
|
|
13546
13597
|
/** Resolved default focus target. */
|
|
@@ -13552,7 +13603,7 @@ class KjConfirmPopup {
|
|
|
13552
13603
|
constructor() {
|
|
13553
13604
|
// Bridge the controller's open signal into the resolution contract.
|
|
13554
13605
|
effect(() => {
|
|
13555
|
-
const isOpen = this.
|
|
13606
|
+
const isOpen = this.controllerOf()?.isOpen() ?? false;
|
|
13556
13607
|
if (isOpen && !this.wasOpen) {
|
|
13557
13608
|
this.resolved = false;
|
|
13558
13609
|
}
|
|
@@ -13568,7 +13619,8 @@ class KjConfirmPopup {
|
|
|
13568
13619
|
* Close the popup with the given result. `true` confirms, `false` cancels.
|
|
13569
13620
|
*/
|
|
13570
13621
|
close(result) {
|
|
13571
|
-
|
|
13622
|
+
const controller = this.controllerOf();
|
|
13623
|
+
if (!controller || !controller.isOpen())
|
|
13572
13624
|
return;
|
|
13573
13625
|
this.resolved = true;
|
|
13574
13626
|
if (result)
|
|
@@ -13576,7 +13628,7 @@ class KjConfirmPopup {
|
|
|
13576
13628
|
else
|
|
13577
13629
|
this.kjCancelled.emit();
|
|
13578
13630
|
this.kjResult.emit(result);
|
|
13579
|
-
|
|
13631
|
+
controller.close('programmatic');
|
|
13580
13632
|
}
|
|
13581
13633
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjConfirmPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
13582
13634
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjConfirmPopup, isStandalone: true, selector: "[kjConfirmPopup]", inputs: { kjDestructive: { classPropertyName: "kjDestructive", publicName: "kjDestructive", isSignal: true, isRequired: false, transformFunction: null }, kjDefaultFocus: { classPropertyName: "kjDefaultFocus", publicName: "kjDefaultFocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { kjConfirmed: "kjConfirmed", kjCancelled: "kjCancelled", kjResult: "kjResult" }, providers: [
|
|
@@ -13604,6 +13656,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
13604
13656
|
*/
|
|
13605
13657
|
class KjConfirmPopupTrigger {
|
|
13606
13658
|
_overlayTrigger = inject(KjOverlayTrigger, { self: true });
|
|
13659
|
+
constructor() {
|
|
13660
|
+
// The trigger normally sits on a CHILD of `[kjConfirmPopup]`, so the root
|
|
13661
|
+
// cannot see this controller through its own injector. Hand it over, or
|
|
13662
|
+
// the confirm/cancel slots resolve nothing.
|
|
13663
|
+
inject(KJ_CONFIRM_POPUP, { optional: true })?._registerController?.(this._overlayTrigger.controller);
|
|
13664
|
+
}
|
|
13607
13665
|
/** Forwarded controller for sibling `[kjFor]` panels. */
|
|
13608
13666
|
get controller() {
|
|
13609
13667
|
return this._overlayTrigger.controller;
|
|
@@ -13622,7 +13680,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
13622
13680
|
exportAs: 'kjConfirmPopupTrigger',
|
|
13623
13681
|
hostDirectives: [KjPopoverTrigger],
|
|
13624
13682
|
}]
|
|
13625
|
-
}] });
|
|
13683
|
+
}], ctorParameters: () => [] });
|
|
13626
13684
|
|
|
13627
13685
|
/**
|
|
13628
13686
|
* The floating confirm popup panel. Composes `kj-popover-content` (a
|
|
@@ -15576,12 +15634,20 @@ class KjCommandPalette {
|
|
|
15576
15634
|
}
|
|
15577
15635
|
});
|
|
15578
15636
|
});
|
|
15579
|
-
//
|
|
15637
|
+
// Re-seed when the visible set changes under the active item: items may
|
|
15638
|
+
// register asynchronously (nothing highlighted yet), and with
|
|
15639
|
+
// `[kjShouldFilter]="false"` the consumer re-renders the list AFTER the
|
|
15640
|
+
// query effect above ran — so that effect highlighted an item that is
|
|
15641
|
+
// already gone. Without the `some()` check the palette kept a stale value,
|
|
15642
|
+
// no row was active, and Enter did nothing (reproducible by typing fast
|
|
15643
|
+
// or pasting a query).
|
|
15580
15644
|
effect(() => {
|
|
15581
15645
|
const visible = this.visibleItems();
|
|
15582
15646
|
const active = this.kjValue();
|
|
15583
15647
|
const autoFirst = this.kjAutoActivateFirst();
|
|
15584
|
-
if (!autoFirst || visible.length === 0
|
|
15648
|
+
if (!autoFirst || visible.length === 0)
|
|
15649
|
+
return;
|
|
15650
|
+
if (active !== null && visible.some(i => i.value() === active))
|
|
15585
15651
|
return;
|
|
15586
15652
|
untracked(() => {
|
|
15587
15653
|
const nav = this._nav();
|