@mintplayer/ng-bootstrap 21.30.0 → 21.31.0
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.
- package/fesm2022/mintplayer-ng-bootstrap-a11y.mjs +455 -0
- package/fesm2022/mintplayer-ng-bootstrap-a11y.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-bootstrap-accordion.mjs +8 -5
- package/fesm2022/mintplayer-ng-bootstrap-accordion.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-breadcrumb.mjs +10 -4
- package/fesm2022/mintplayer-ng-bootstrap-breadcrumb.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-button-group.mjs +7 -4
- package/fesm2022/mintplayer-ng-bootstrap-button-group.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-calendar.mjs +131 -3
- package/fesm2022/mintplayer-ng-bootstrap-calendar.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-carousel.mjs +80 -48
- package/fesm2022/mintplayer-ng-bootstrap-carousel.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-code-snippet.mjs +4 -1
- package/fesm2022/mintplayer-ng-bootstrap-code-snippet.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-color-picker.mjs +218 -14
- package/fesm2022/mintplayer-ng-bootstrap-color-picker.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-datatable.mjs +4 -3
- package/fesm2022/mintplayer-ng-bootstrap-datatable.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-datepicker.mjs +2 -2
- package/fesm2022/mintplayer-ng-bootstrap-datepicker.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-dock.mjs +294 -3
- package/fesm2022/mintplayer-ng-bootstrap-dock.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-dropdown-menu.mjs +163 -18
- package/fesm2022/mintplayer-ng-bootstrap-dropdown-menu.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-dropdown.mjs +179 -7
- package/fesm2022/mintplayer-ng-bootstrap-dropdown.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-file-upload.mjs +14 -4
- package/fesm2022/mintplayer-ng-bootstrap-file-upload.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-has-overlay.mjs +14 -0
- package/fesm2022/mintplayer-ng-bootstrap-has-overlay.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-list-group.mjs +2 -1
- package/fesm2022/mintplayer-ng-bootstrap-list-group.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-marquee.mjs +7 -4
- package/fesm2022/mintplayer-ng-bootstrap-marquee.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-modal.mjs +70 -6
- package/fesm2022/mintplayer-ng-bootstrap-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-multiselect.mjs +5 -4
- package/fesm2022/mintplayer-ng-bootstrap-multiselect.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-navbar-toggler.mjs +6 -6
- package/fesm2022/mintplayer-ng-bootstrap-navbar-toggler.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-navbar.mjs +45 -13
- package/fesm2022/mintplayer-ng-bootstrap-navbar.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-offcanvas.mjs +51 -5
- package/fesm2022/mintplayer-ng-bootstrap-offcanvas.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-pagination.mjs +5 -3
- package/fesm2022/mintplayer-ng-bootstrap-pagination.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-placeholder.mjs +18 -4
- package/fesm2022/mintplayer-ng-bootstrap-placeholder.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-playlist-toggler.mjs +6 -6
- package/fesm2022/mintplayer-ng-bootstrap-playlist-toggler.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-popover.mjs +61 -6
- package/fesm2022/mintplayer-ng-bootstrap-popover.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-priority-nav.mjs +19 -4
- package/fesm2022/mintplayer-ng-bootstrap-priority-nav.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-progress-bar.mjs +8 -5
- package/fesm2022/mintplayer-ng-bootstrap-progress-bar.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-range.mjs +4 -3
- package/fesm2022/mintplayer-ng-bootstrap-range.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-rating.mjs +34 -4
- package/fesm2022/mintplayer-ng-bootstrap-rating.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-reduced-motion.mjs +59 -0
- package/fesm2022/mintplayer-ng-bootstrap-reduced-motion.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-bootstrap-resizable.mjs +91 -2
- package/fesm2022/mintplayer-ng-bootstrap-resizable.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-scheduler.mjs +16 -5
- package/fesm2022/mintplayer-ng-bootstrap-scheduler.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-scrollspy.mjs +2 -2
- package/fesm2022/mintplayer-ng-bootstrap-scrollspy.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-searchbox.mjs +28 -5
- package/fesm2022/mintplayer-ng-bootstrap-searchbox.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-select.mjs +4 -3
- package/fesm2022/mintplayer-ng-bootstrap-select.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-select2.mjs +18 -4
- package/fesm2022/mintplayer-ng-bootstrap-select2.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-signature-pad.mjs +4 -3
- package/fesm2022/mintplayer-ng-bootstrap-signature-pad.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-tab-control.mjs +2 -2
- package/fesm2022/mintplayer-ng-bootstrap-tab-control.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-table.mjs +10 -3
- package/fesm2022/mintplayer-ng-bootstrap-table.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-tile-manager.mjs +143 -29
- package/fesm2022/mintplayer-ng-bootstrap-tile-manager.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-timepicker.mjs +2 -2
- package/fesm2022/mintplayer-ng-bootstrap-timepicker.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-toast.mjs +7 -4
- package/fesm2022/mintplayer-ng-bootstrap-toast.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-toggle-button.mjs +42 -21
- package/fesm2022/mintplayer-ng-bootstrap-toggle-button.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-tooltip.mjs +33 -4
- package/fesm2022/mintplayer-ng-bootstrap-tooltip.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-treeview.mjs +17 -7
- package/fesm2022/mintplayer-ng-bootstrap-treeview.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-typeahead.mjs +50 -8
- package/fesm2022/mintplayer-ng-bootstrap-typeahead.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-virtual-datatable.mjs +34 -12
- package/fesm2022/mintplayer-ng-bootstrap-virtual-datatable.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-web-components-a11y.mjs +74 -0
- package/fesm2022/mintplayer-ng-bootstrap-web-components-a11y.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-bootstrap-web-components-scheduler.mjs +1476 -71
- package/fesm2022/mintplayer-ng-bootstrap-web-components-scheduler.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-web-components-splitter.mjs +194 -2
- package/fesm2022/mintplayer-ng-bootstrap-web-components-splitter.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-bootstrap-web-components-tab-control.mjs +4 -0
- package/fesm2022/mintplayer-ng-bootstrap-web-components-tab-control.mjs.map +1 -1
- package/package.json +14 -2
- package/types/mintplayer-ng-bootstrap-a11y.d.ts +196 -0
- package/types/mintplayer-ng-bootstrap-accordion.d.ts +4 -2
- package/types/mintplayer-ng-bootstrap-breadcrumb.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-button-group.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-calendar.d.ts +32 -0
- package/types/mintplayer-ng-bootstrap-carousel.d.ts +56 -3
- package/types/mintplayer-ng-bootstrap-code-snippet.d.ts +1 -0
- package/types/mintplayer-ng-bootstrap-color-picker.d.ts +75 -4
- package/types/mintplayer-ng-bootstrap-datatable.d.ts +1 -1
- package/types/mintplayer-ng-bootstrap-dock.d.ts +51 -0
- package/types/mintplayer-ng-bootstrap-dropdown-menu.d.ts +54 -9
- package/types/mintplayer-ng-bootstrap-dropdown.d.ts +57 -2
- package/types/mintplayer-ng-bootstrap-file-upload.d.ts +4 -1
- package/types/mintplayer-ng-bootstrap-has-overlay.d.ts +14 -0
- package/types/mintplayer-ng-bootstrap-marquee.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-modal.d.ts +25 -1
- package/types/mintplayer-ng-bootstrap-multiselect.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-navbar-toggler.d.ts +4 -2
- package/types/mintplayer-ng-bootstrap-navbar.d.ts +25 -1
- package/types/mintplayer-ng-bootstrap-offcanvas.d.ts +23 -1
- package/types/mintplayer-ng-bootstrap-pagination.d.ts +3 -1
- package/types/mintplayer-ng-bootstrap-placeholder.d.ts +5 -1
- package/types/mintplayer-ng-bootstrap-playlist-toggler.d.ts +4 -2
- package/types/mintplayer-ng-bootstrap-popover.d.ts +21 -1
- package/types/mintplayer-ng-bootstrap-priority-nav.d.ts +4 -1
- package/types/mintplayer-ng-bootstrap-progress-bar.d.ts +4 -2
- package/types/mintplayer-ng-bootstrap-range.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-rating.d.ts +3 -0
- package/types/mintplayer-ng-bootstrap-reduced-motion.d.ts +36 -0
- package/types/mintplayer-ng-bootstrap-resizable.d.ts +4 -0
- package/types/mintplayer-ng-bootstrap-scheduler.d.ts +42 -9
- package/types/mintplayer-ng-bootstrap-scrollspy.d.ts +1 -1
- package/types/mintplayer-ng-bootstrap-searchbox.d.ts +8 -1
- package/types/mintplayer-ng-bootstrap-select.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-select2.d.ts +3 -0
- package/types/mintplayer-ng-bootstrap-signature-pad.d.ts +2 -1
- package/types/mintplayer-ng-bootstrap-table.d.ts +8 -1
- package/types/mintplayer-ng-bootstrap-tile-manager.d.ts +21 -2
- package/types/mintplayer-ng-bootstrap-toast.d.ts +6 -1
- package/types/mintplayer-ng-bootstrap-toggle-button.d.ts +11 -0
- package/types/mintplayer-ng-bootstrap-tooltip.d.ts +5 -0
- package/types/mintplayer-ng-bootstrap-treeview.d.ts +12 -1
- package/types/mintplayer-ng-bootstrap-typeahead.d.ts +11 -3
- package/types/mintplayer-ng-bootstrap-virtual-datatable.d.ts +14 -1
- package/types/mintplayer-ng-bootstrap-web-components-a11y.d.ts +34 -0
- package/types/mintplayer-ng-bootstrap-web-components-scheduler-core.d.ts +35 -11
- package/types/mintplayer-ng-bootstrap-web-components-scheduler.d.ts +246 -0
- package/types/mintplayer-ng-bootstrap-web-components-splitter.d.ts +95 -37
|
@@ -2,6 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { input, output, viewChild, ChangeDetectionStrategy, Component, contentChildren, computed, effect, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
3
3
|
import { NgTemplateOutlet } from '@angular/common';
|
|
4
4
|
import { html, unsafeCSS, LitElement, nothing } from 'lit';
|
|
5
|
+
import { LiveAnnouncerController } from '@mintplayer/ng-bootstrap/web-components/a11y';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* One tile inside a `<bs-tile-manager>`.
|
|
@@ -382,7 +383,7 @@ const styles = unsafeCSS(`:host {
|
|
|
382
383
|
|
|
383
384
|
.tile[data-resizing=true] {
|
|
384
385
|
z-index: 4;
|
|
385
|
-
transition:
|
|
386
|
+
transition: width 150ms cubic-bezier(0.2, 0, 0, 1), height 150ms cubic-bezier(0.2, 0, 0, 1);
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
.tile[data-blocked=true] {
|
|
@@ -504,7 +505,8 @@ const styles = unsafeCSS(`:host {
|
|
|
504
505
|
display: none;
|
|
505
506
|
}
|
|
506
507
|
|
|
507
|
-
.tile-grid__live-region
|
|
508
|
+
.tile-grid__live-region,
|
|
509
|
+
.tile-grid__sr-only {
|
|
508
510
|
position: absolute;
|
|
509
511
|
width: 1px;
|
|
510
512
|
height: 1px;
|
|
@@ -534,6 +536,8 @@ const styles = unsafeCSS(`:host {
|
|
|
534
536
|
outline-offset: -2px;
|
|
535
537
|
}`);
|
|
536
538
|
|
|
539
|
+
const TILE_INSTRUCTIONS = 'Press M to enter move mode. In move mode, arrow keys move the tile, Shift with arrow keys resize it, Enter commits, Escape cancels.';
|
|
540
|
+
let tileManagerInstanceCounter = 0;
|
|
537
541
|
const TOUCH_LONG_PRESS_MS = 600;
|
|
538
542
|
const TOUCH_LONG_PRESS_SLOP_PX = 10;
|
|
539
543
|
const TOUCH_PRESS_FEEDBACK_DELAY_MS = 150;
|
|
@@ -557,7 +561,9 @@ class MintTileManagerElement extends LitElement {
|
|
|
557
561
|
this.gestureKind = 'idle';
|
|
558
562
|
this.blocked = false;
|
|
559
563
|
this.effectiveColumnCount = 1;
|
|
560
|
-
this.
|
|
564
|
+
this.focusedTileId = null;
|
|
565
|
+
this.liveAnnouncer = new LiveAnnouncerController(this);
|
|
566
|
+
this.instanceId = `mp-tile-manager-${++tileManagerInstanceCounter}`;
|
|
561
567
|
this.hostResizeObserver = null;
|
|
562
568
|
this.flipPreviousRects = new Map();
|
|
563
569
|
// Cached layout metrics. Refreshed lazily in updated()/firstUpdated() and on
|
|
@@ -605,27 +611,36 @@ class MintTileManagerElement extends LitElement {
|
|
|
605
611
|
blocked: { state: true },
|
|
606
612
|
effectiveColumnCount: { state: true },
|
|
607
613
|
keyboardMode: { state: true },
|
|
608
|
-
|
|
614
|
+
focusedTileId: { state: true },
|
|
609
615
|
}; }
|
|
616
|
+
get instructionsId() {
|
|
617
|
+
return `${this.instanceId}-instructions`;
|
|
618
|
+
}
|
|
610
619
|
render() {
|
|
611
620
|
const layoutSource = this.previewLayout ?? this.tiles.map((t) => ({ id: t.id, position: t.position }));
|
|
612
621
|
const tileById = new Map(this.tiles.map((t) => [t.id, t]));
|
|
613
622
|
const gridStyle = this.computeGridStyle();
|
|
614
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
623
|
+
// role="region" + button-per-tile (PRD §10 Q1): a tile board's dimensions
|
|
624
|
+
// change under user reflow, so the static grid contract (rowindex/colindex)
|
|
625
|
+
// is a poor fit. Tiles are activatable buttons whose move/resize is
|
|
626
|
+
// discoverable through aria-describedby's instructions string.
|
|
617
627
|
return html `
|
|
618
|
-
<div
|
|
619
|
-
|
|
620
|
-
|
|
628
|
+
<div
|
|
629
|
+
class="tile-grid"
|
|
630
|
+
role="region"
|
|
631
|
+
aria-label=${this.label ?? 'Tile board'}
|
|
632
|
+
aria-describedby=${this.instructionsId}
|
|
633
|
+
style=${gridStyle}
|
|
634
|
+
>
|
|
635
|
+
${layoutSource.map((entry) => {
|
|
621
636
|
const tile = tileById.get(entry.id);
|
|
622
637
|
if (!tile)
|
|
623
638
|
return nothing;
|
|
624
639
|
return this.renderTile(tile, entry.position);
|
|
625
640
|
})}
|
|
626
|
-
</div>
|
|
627
641
|
</div>
|
|
628
|
-
<div class="tile-
|
|
642
|
+
<div id=${this.instructionsId} class="tile-grid__sr-only">${TILE_INSTRUCTIONS}</div>
|
|
643
|
+
${this.liveAnnouncer.template()}
|
|
629
644
|
`;
|
|
630
645
|
}
|
|
631
646
|
renderTile(tile, pos) {
|
|
@@ -634,18 +649,34 @@ class MintTileManagerElement extends LitElement {
|
|
|
634
649
|
const isBlocked = (isDragging || isResizing) && this.blocked;
|
|
635
650
|
const isPressing = this.gestureKind === 'arming-touch-drag' && this.activeTileId() === tile.id;
|
|
636
651
|
const transform = this.computeActiveTransform(tile.id);
|
|
652
|
+
// Inline width/height during a pointer-resize gesture so the tile pixel
|
|
653
|
+
// size can ride a CSS transition between span states. The grid still
|
|
654
|
+
// allocates the new span area instantly; the active tile's visible size
|
|
655
|
+
// lags 150ms behind. Without this, grid-column/grid-row span changes are
|
|
656
|
+
// not animatable in CSS and the tile snaps. Reduced-motion is gated by
|
|
657
|
+
// the global `@media (prefers-reduced-motion: reduce)` rule in SCSS.
|
|
658
|
+
const cell = this.cellMetrics;
|
|
659
|
+
const inlineSize = isResizing && cell.width > 0
|
|
660
|
+
? {
|
|
661
|
+
width: pos.colSpan * cell.width + (pos.colSpan - 1) * cell.gapX,
|
|
662
|
+
height: pos.rowSpan * cell.height + (pos.rowSpan - 1) * cell.gapY,
|
|
663
|
+
}
|
|
664
|
+
: null;
|
|
637
665
|
const style = [
|
|
638
666
|
`grid-column: ${pos.colStart} / span ${pos.colSpan}`,
|
|
639
667
|
`grid-row: ${pos.rowStart} / span ${pos.rowSpan}`,
|
|
640
668
|
transform ? `transform: ${transform}` : '',
|
|
669
|
+
inlineSize ? `width: ${inlineSize.width}px` : '',
|
|
670
|
+
inlineSize ? `height: ${inlineSize.height}px` : '',
|
|
641
671
|
]
|
|
642
672
|
.filter(Boolean)
|
|
643
673
|
.join('; ');
|
|
674
|
+
const isFocusableStop = this.resolveFocusableTileId() === tile.id;
|
|
644
675
|
return html `
|
|
645
676
|
<div
|
|
646
677
|
class="tile"
|
|
647
|
-
role="
|
|
648
|
-
tabindex
|
|
678
|
+
role="button"
|
|
679
|
+
tabindex=${isFocusableStop ? '0' : '-1'}
|
|
649
680
|
data-tile-id=${tile.id}
|
|
650
681
|
data-dragging=${isDragging ? 'true' : 'false'}
|
|
651
682
|
data-resizing=${isResizing ? 'true' : 'false'}
|
|
@@ -658,6 +689,7 @@ class MintTileManagerElement extends LitElement {
|
|
|
658
689
|
style=${style}
|
|
659
690
|
@pointerdown=${(e) => this.onTilePointerDown(e, tile)}
|
|
660
691
|
@keydown=${(e) => this.onTileKeyDown(e, tile)}
|
|
692
|
+
@focus=${() => this.onTileFocus(tile)}
|
|
661
693
|
>
|
|
662
694
|
<div class="tile__header-shell">
|
|
663
695
|
<slot name=${`${tile.id}-header`}></slot>
|
|
@@ -742,9 +774,6 @@ class MintTileManagerElement extends LitElement {
|
|
|
742
774
|
// ---------------- Lifecycle ----------------
|
|
743
775
|
connectedCallback() {
|
|
744
776
|
super.connectedCallback();
|
|
745
|
-
if (!this.hasAttribute('role')) {
|
|
746
|
-
this.setAttribute('role', 'application');
|
|
747
|
-
}
|
|
748
777
|
document.addEventListener('visibilitychange', this.onVisibilityChange);
|
|
749
778
|
}
|
|
750
779
|
disconnectedCallback() {
|
|
@@ -1005,6 +1034,7 @@ class MintTileManagerElement extends LitElement {
|
|
|
1005
1034
|
this.gestureKind = 'drag';
|
|
1006
1035
|
this.lastPointerPosition = { x: startX, y: startY };
|
|
1007
1036
|
this.attachWindowListeners();
|
|
1037
|
+
this.announceDragBegin(tile);
|
|
1008
1038
|
this.requestUpdate();
|
|
1009
1039
|
}
|
|
1010
1040
|
beginDrag(event, tile) {
|
|
@@ -1027,6 +1057,7 @@ class MintTileManagerElement extends LitElement {
|
|
|
1027
1057
|
this.gestureKind = 'drag';
|
|
1028
1058
|
this.lastPointerPosition = { x: event.clientX, y: event.clientY };
|
|
1029
1059
|
this.attachWindowListeners();
|
|
1060
|
+
this.announceDragBegin(tile);
|
|
1030
1061
|
this.runPackerForCurrentGesture();
|
|
1031
1062
|
}
|
|
1032
1063
|
beginResize(event, tile, mode) {
|
|
@@ -1043,8 +1074,17 @@ class MintTileManagerElement extends LitElement {
|
|
|
1043
1074
|
this.gestureKind = 'resize';
|
|
1044
1075
|
this.lastPointerPosition = { x: event.clientX, y: event.clientY };
|
|
1045
1076
|
this.attachWindowListeners();
|
|
1077
|
+
this.announceResizeBegin(tile);
|
|
1046
1078
|
this.runPackerForCurrentGesture();
|
|
1047
1079
|
}
|
|
1080
|
+
announceDragBegin(tile) {
|
|
1081
|
+
const label = tile.label ?? `tile at row ${tile.position.rowStart}, column ${tile.position.colStart}`;
|
|
1082
|
+
this.liveAnnouncer.announce(`Dragging ${label}.`);
|
|
1083
|
+
}
|
|
1084
|
+
announceResizeBegin(tile) {
|
|
1085
|
+
const label = tile.label ?? `tile at row ${tile.position.rowStart}, column ${tile.position.colStart}`;
|
|
1086
|
+
this.liveAnnouncer.announce(`Resizing ${label}.`);
|
|
1087
|
+
}
|
|
1048
1088
|
attachWindowListeners() {
|
|
1049
1089
|
window.addEventListener('pointermove', this.onWindowPointerMove);
|
|
1050
1090
|
window.addEventListener('pointerup', this.onWindowPointerUp);
|
|
@@ -1110,7 +1150,11 @@ class MintTileManagerElement extends LitElement {
|
|
|
1110
1150
|
const cols = this.effectiveColumnCount;
|
|
1111
1151
|
const result = pack(this.tiles.map((t) => ({ id: t.id, position: t.position, locked: t.disableMove })), { id: tile.id, rect }, cols);
|
|
1112
1152
|
this.previewLayout = result.layout;
|
|
1153
|
+
const wasBlocked = g.blocked;
|
|
1113
1154
|
g.blocked = result.blocked;
|
|
1155
|
+
if (result.blocked && !wasBlocked) {
|
|
1156
|
+
this.liveAnnouncer.announce('Move blocked by a locked tile.');
|
|
1157
|
+
}
|
|
1114
1158
|
this.blocked = result.blocked;
|
|
1115
1159
|
this.requestUpdate();
|
|
1116
1160
|
}
|
|
@@ -1191,10 +1235,9 @@ class MintTileManagerElement extends LitElement {
|
|
|
1191
1235
|
}));
|
|
1192
1236
|
const movedTile = finalLayout.find((p) => p.id === g.tileId);
|
|
1193
1237
|
if (movedTile) {
|
|
1194
|
-
this.
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
: `Tile resized to ${movedTile.position.colSpan} columns by ${movedTile.position.rowSpan} rows`;
|
|
1238
|
+
this.liveAnnouncer.announce(g.kind === 'drag'
|
|
1239
|
+
? `Tile moved to row ${movedTile.position.rowStart}, column ${movedTile.position.colStart}`
|
|
1240
|
+
: `Tile resized to ${movedTile.position.colSpan} columns by ${movedTile.position.rowSpan} rows`);
|
|
1198
1241
|
}
|
|
1199
1242
|
this.cleanupGesture();
|
|
1200
1243
|
}
|
|
@@ -1250,16 +1293,20 @@ class MintTileManagerElement extends LitElement {
|
|
|
1250
1293
|
}
|
|
1251
1294
|
// ---------------- Keyboard ----------------
|
|
1252
1295
|
onTileKeyDown(event, tile) {
|
|
1253
|
-
if (tile.disableMove && tile.disableResize)
|
|
1254
|
-
return;
|
|
1255
1296
|
const km = this.keyboardState;
|
|
1256
1297
|
if (km.kind === 'idle') {
|
|
1257
|
-
|
|
1298
|
+
// Outside move mode: arrow keys traverse focus between tiles, Home/End
|
|
1299
|
+
// jump to first/last, M enters move mode if the tile allows it.
|
|
1300
|
+
if (event.key === 'm' || event.key === 'M') {
|
|
1301
|
+
if (tile.disableMove && tile.disableResize)
|
|
1302
|
+
return;
|
|
1258
1303
|
event.preventDefault();
|
|
1259
1304
|
this.keyboardState = { kind: 'move', tileId: tile.id };
|
|
1260
|
-
this.
|
|
1305
|
+
this.liveAnnouncer.announce('Move mode enabled. Use arrow keys to move, Shift with arrow keys to resize, Enter to commit, Escape to cancel.');
|
|
1261
1306
|
return;
|
|
1262
1307
|
}
|
|
1308
|
+
if (this.handleFocusNavigation(event, tile))
|
|
1309
|
+
return;
|
|
1263
1310
|
return;
|
|
1264
1311
|
}
|
|
1265
1312
|
if (km.tileId !== tile.id)
|
|
@@ -1267,7 +1314,7 @@ class MintTileManagerElement extends LitElement {
|
|
|
1267
1314
|
if (event.key === 'Escape' || event.key === 'Enter') {
|
|
1268
1315
|
event.preventDefault();
|
|
1269
1316
|
this.keyboardState = { kind: 'idle' };
|
|
1270
|
-
this.
|
|
1317
|
+
this.liveAnnouncer.announce(event.key === 'Enter' ? 'Move committed.' : 'Move cancelled.');
|
|
1271
1318
|
return;
|
|
1272
1319
|
}
|
|
1273
1320
|
if (event.key.startsWith('Arrow')) {
|
|
@@ -1276,6 +1323,73 @@ class MintTileManagerElement extends LitElement {
|
|
|
1276
1323
|
this.applyKeyboardStep(tile, event.key, isResize);
|
|
1277
1324
|
}
|
|
1278
1325
|
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Outside move mode, arrow keys move *focus* between tiles (row-major
|
|
1328
|
+
* order — top-to-bottom, left-to-right by gridcell position). Home/End
|
|
1329
|
+
* jump to first/last enabled tile. Returns true if the event was handled.
|
|
1330
|
+
*/
|
|
1331
|
+
handleFocusNavigation(event, current) {
|
|
1332
|
+
const navKey = event.key === 'ArrowUp' || event.key === 'ArrowDown' ||
|
|
1333
|
+
event.key === 'ArrowLeft' || event.key === 'ArrowRight' ||
|
|
1334
|
+
event.key === 'Home' || event.key === 'End';
|
|
1335
|
+
if (!navKey)
|
|
1336
|
+
return false;
|
|
1337
|
+
if (this.tiles.length <= 1)
|
|
1338
|
+
return false;
|
|
1339
|
+
const ordered = [...this.tiles].sort((a, b) => {
|
|
1340
|
+
if (a.position.rowStart !== b.position.rowStart)
|
|
1341
|
+
return a.position.rowStart - b.position.rowStart;
|
|
1342
|
+
return a.position.colStart - b.position.colStart;
|
|
1343
|
+
});
|
|
1344
|
+
const idx = ordered.findIndex((t) => t.id === current.id);
|
|
1345
|
+
if (idx < 0)
|
|
1346
|
+
return false;
|
|
1347
|
+
let nextIdx = idx;
|
|
1348
|
+
if (event.key === 'Home')
|
|
1349
|
+
nextIdx = 0;
|
|
1350
|
+
else if (event.key === 'End')
|
|
1351
|
+
nextIdx = ordered.length - 1;
|
|
1352
|
+
else if (event.key === 'ArrowRight' || event.key === 'ArrowDown')
|
|
1353
|
+
nextIdx = (idx + 1) % ordered.length;
|
|
1354
|
+
else
|
|
1355
|
+
nextIdx = (idx - 1 + ordered.length) % ordered.length;
|
|
1356
|
+
if (nextIdx === idx)
|
|
1357
|
+
return true;
|
|
1358
|
+
event.preventDefault();
|
|
1359
|
+
const target = ordered[nextIdx];
|
|
1360
|
+
this.focusedTileId = target.id;
|
|
1361
|
+
this.requestUpdate();
|
|
1362
|
+
queueMicrotask(() => {
|
|
1363
|
+
const el = this.shadowRoot?.querySelector(`.tile[data-tile-id="${target.id}"]`);
|
|
1364
|
+
el?.focus();
|
|
1365
|
+
});
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1368
|
+
onTileFocus(tile) {
|
|
1369
|
+
if (this.focusedTileId !== tile.id) {
|
|
1370
|
+
this.focusedTileId = tile.id;
|
|
1371
|
+
this.requestUpdate();
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* The single tile that should carry `tabindex="0"` to act as the board's
|
|
1376
|
+
* tab stop. Defaults to the previously-focused tile, then the first tile in
|
|
1377
|
+
* row-major order. All other tiles render with `tabindex="-1"` so Tab from
|
|
1378
|
+
* outside the board lands on exactly one tile.
|
|
1379
|
+
*/
|
|
1380
|
+
resolveFocusableTileId() {
|
|
1381
|
+
if (this.tiles.length === 0)
|
|
1382
|
+
return null;
|
|
1383
|
+
if (this.focusedTileId && this.tiles.some((t) => t.id === this.focusedTileId)) {
|
|
1384
|
+
return this.focusedTileId;
|
|
1385
|
+
}
|
|
1386
|
+
const ordered = [...this.tiles].sort((a, b) => {
|
|
1387
|
+
if (a.position.rowStart !== b.position.rowStart)
|
|
1388
|
+
return a.position.rowStart - b.position.rowStart;
|
|
1389
|
+
return a.position.colStart - b.position.colStart;
|
|
1390
|
+
});
|
|
1391
|
+
return ordered[0]?.id ?? null;
|
|
1392
|
+
}
|
|
1279
1393
|
applyKeyboardStep(tile, key, isResize) {
|
|
1280
1394
|
const cols = this.effectiveColumnCount;
|
|
1281
1395
|
const dx = key === 'ArrowLeft' ? -1 : key === 'ArrowRight' ? 1 : 0;
|
|
@@ -1295,7 +1409,7 @@ class MintTileManagerElement extends LitElement {
|
|
|
1295
1409
|
};
|
|
1296
1410
|
const result = pack(this.tiles.map((t) => ({ id: t.id, position: t.position, locked: t.disableMove })), { id: tile.id, rect: newRect }, cols);
|
|
1297
1411
|
if (result.blocked) {
|
|
1298
|
-
this.
|
|
1412
|
+
this.liveAnnouncer.announce('Move blocked.');
|
|
1299
1413
|
return;
|
|
1300
1414
|
}
|
|
1301
1415
|
const newTiles = this.tiles.map((t) => {
|
|
@@ -1315,9 +1429,9 @@ class MintTileManagerElement extends LitElement {
|
|
|
1315
1429
|
composed: true,
|
|
1316
1430
|
}));
|
|
1317
1431
|
});
|
|
1318
|
-
this.
|
|
1432
|
+
this.liveAnnouncer.announce(isResize
|
|
1319
1433
|
? `Tile resized to ${newRect.colSpan} columns by ${newRect.rowSpan} rows`
|
|
1320
|
-
: `Tile moved to row ${newRect.rowStart}, column ${newRect.colStart}
|
|
1434
|
+
: `Tile moved to row ${newRect.rowStart}, column ${newRect.colStart}`);
|
|
1321
1435
|
}
|
|
1322
1436
|
}
|
|
1323
1437
|
if (typeof customElements !== 'undefined' && !customElements.get('mp-tile-manager')) {
|