@alaarab/ogrid-angular 2.0.2

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.
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, output } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ let EmptyStateComponent = class EmptyStateComponent {
10
+ constructor() {
11
+ this.message = input(undefined);
12
+ this.hasActiveFilters = input(false);
13
+ this.render = input(undefined);
14
+ this.clearAll = output();
15
+ }
16
+ };
17
+ EmptyStateComponent = __decorate([
18
+ Component({
19
+ selector: 'ogrid-empty-state',
20
+ standalone: true,
21
+ imports: [CommonModule],
22
+ template: `
23
+ @if (render()) {
24
+ <ng-container [ngTemplateOutlet]="render()!"></ng-container>
25
+ } @else if (message()) {
26
+ {{ message() }}
27
+ } @else if (hasActiveFilters()) {
28
+ No items match your current filters. Try adjusting your search or
29
+ <button type="button" (click)="clearAll.emit()" style="background:none;border:none;color:inherit;text-decoration:underline;cursor:pointer;padding:0;font:inherit">
30
+ clear all filters
31
+ </button>
32
+ to see all items.
33
+ } @else {
34
+ There are no items available at this time.
35
+ }
36
+ `,
37
+ })
38
+ ], EmptyStateComponent);
39
+ export { EmptyStateComponent };
@@ -0,0 +1,117 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, output, effect, viewChild, DestroyRef, inject } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ import { GRID_CONTEXT_MENU_ITEMS, formatShortcut } from '@alaarab/ogrid-core';
10
+ let GridContextMenuComponent = class GridContextMenuComponent {
11
+ constructor() {
12
+ this.destroyRef = inject(DestroyRef);
13
+ this.x = input.required();
14
+ this.y = input.required();
15
+ this.hasSelection = input(false);
16
+ this.canUndoProp = input(false);
17
+ this.canRedoProp = input(false);
18
+ this.classNames = input(undefined);
19
+ this.copy = output();
20
+ this.cut = output();
21
+ this.paste = output();
22
+ this.selectAll = output();
23
+ this.undoAction = output();
24
+ this.redoAction = output();
25
+ this.close = output();
26
+ this.menuRef = viewChild('menuRef');
27
+ this.menuItems = GRID_CONTEXT_MENU_ITEMS;
28
+ this.formatShortcutFn = formatShortcut;
29
+ this.clickOutsideHandler = (e) => {
30
+ const el = this.menuRef()?.nativeElement;
31
+ if (el && !el.contains(e.target))
32
+ this.close.emit();
33
+ };
34
+ this.keyDownHandler = (e) => {
35
+ if (e.key === 'Escape')
36
+ this.close.emit();
37
+ };
38
+ effect(() => {
39
+ // Re-register listeners when component renders
40
+ document.addEventListener('mousedown', this.clickOutsideHandler, true);
41
+ document.addEventListener('keydown', this.keyDownHandler, true);
42
+ });
43
+ this.destroyRef.onDestroy(() => {
44
+ document.removeEventListener('mousedown', this.clickOutsideHandler, true);
45
+ document.removeEventListener('keydown', this.keyDownHandler, true);
46
+ });
47
+ }
48
+ isDisabled(item) {
49
+ if (item.disabledWhenNoSelection && !this.hasSelection())
50
+ return true;
51
+ if (item.id === 'undo' && !this.canUndoProp())
52
+ return true;
53
+ if (item.id === 'redo' && !this.canRedoProp())
54
+ return true;
55
+ return false;
56
+ }
57
+ onItemClick(id) {
58
+ switch (id) {
59
+ case 'copy':
60
+ this.copy.emit();
61
+ break;
62
+ case 'cut':
63
+ this.cut.emit();
64
+ break;
65
+ case 'paste':
66
+ this.paste.emit();
67
+ break;
68
+ case 'selectAll':
69
+ this.selectAll.emit();
70
+ break;
71
+ case 'undo':
72
+ this.undoAction.emit();
73
+ break;
74
+ case 'redo':
75
+ this.redoAction.emit();
76
+ break;
77
+ }
78
+ this.close.emit();
79
+ }
80
+ };
81
+ GridContextMenuComponent = __decorate([
82
+ Component({
83
+ selector: 'ogrid-context-menu',
84
+ standalone: true,
85
+ imports: [CommonModule],
86
+ template: `
87
+ <div
88
+ #menuRef
89
+ [class]="classNames()?.contextMenu ?? ''"
90
+ role="menu"
91
+ [style.left.px]="x()"
92
+ [style.top.px]="y()"
93
+ aria-label="Grid context menu"
94
+ >
95
+ @for (item of menuItems; track item.id) {
96
+ @if (item.dividerBefore) {
97
+ <div [class]="classNames()?.contextMenuDivider ?? ''"></div>
98
+ }
99
+ <button
100
+ type="button"
101
+ [class]="classNames()?.contextMenuItem ?? ''"
102
+ (click)="onItemClick(item.id)"
103
+ [disabled]="isDisabled(item)"
104
+ >
105
+ <span [class]="classNames()?.contextMenuItemLabel ?? ''">{{ item.label }}</span>
106
+ @if (item.shortcut) {
107
+ <span [class]="classNames()?.contextMenuItemShortcut ?? ''">
108
+ {{ formatShortcutFn(item.shortcut) }}
109
+ </span>
110
+ }
111
+ </button>
112
+ }
113
+ </div>
114
+ `,
115
+ })
116
+ ], GridContextMenuComponent);
117
+ export { GridContextMenuComponent };
@@ -0,0 +1,155 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, effect, signal, DestroyRef, inject } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ function measureRange(container, range, colOffset) {
10
+ const startGlobalCol = range.startCol + colOffset;
11
+ const endGlobalCol = range.endCol + colOffset;
12
+ const topLeft = container.querySelector(`[data-row-index="${range.startRow}"][data-col-index="${startGlobalCol}"]`);
13
+ const bottomRight = container.querySelector(`[data-row-index="${range.endRow}"][data-col-index="${endGlobalCol}"]`);
14
+ if (!topLeft || !bottomRight)
15
+ return null;
16
+ const cRect = container.getBoundingClientRect();
17
+ const tlRect = topLeft.getBoundingClientRect();
18
+ const brRect = bottomRight.getBoundingClientRect();
19
+ return {
20
+ top: tlRect.top - cRect.top,
21
+ left: tlRect.left - cRect.left,
22
+ width: brRect.right - tlRect.left,
23
+ height: brRect.bottom - tlRect.top,
24
+ };
25
+ }
26
+ function ensureKeyframes() {
27
+ if (typeof document === 'undefined')
28
+ return;
29
+ if (document.getElementById('ogrid-marching-ants-keyframes'))
30
+ return;
31
+ const style = document.createElement('style');
32
+ style.id = 'ogrid-marching-ants-keyframes';
33
+ style.textContent = '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}';
34
+ document.head.appendChild(style);
35
+ }
36
+ let MarchingAntsOverlayComponent = class MarchingAntsOverlayComponent {
37
+ constructor() {
38
+ this.destroyRef = inject(DestroyRef);
39
+ this.containerEl = input.required();
40
+ this.selectionRange = input(null);
41
+ this.copyRange = input(null);
42
+ this.cutRange = input(null);
43
+ this.colOffset = input(0);
44
+ this.selRect = signal(null);
45
+ this.clipRect = signal(null);
46
+ this.rafId = 0;
47
+ this.resizeObserver = null;
48
+ ensureKeyframes();
49
+ effect(() => {
50
+ const container = this.containerEl();
51
+ const selRange = this.selectionRange();
52
+ const clipRange = this.copyRange() ?? this.cutRange();
53
+ const colOff = this.colOffset();
54
+ if (this.resizeObserver) {
55
+ this.resizeObserver.disconnect();
56
+ this.resizeObserver = null;
57
+ }
58
+ if (!selRange && !clipRange) {
59
+ this.selRect.set(null);
60
+ this.clipRect.set(null);
61
+ return;
62
+ }
63
+ const measureAll = () => {
64
+ if (!container) {
65
+ this.selRect.set(null);
66
+ this.clipRect.set(null);
67
+ return;
68
+ }
69
+ this.selRect.set(selRange ? measureRange(container, selRange, colOff) : null);
70
+ this.clipRect.set(clipRange ? measureRange(container, clipRange, colOff) : null);
71
+ };
72
+ if (this.rafId)
73
+ cancelAnimationFrame(this.rafId);
74
+ this.rafId = requestAnimationFrame(measureAll);
75
+ if (container) {
76
+ this.resizeObserver = new ResizeObserver(measureAll);
77
+ this.resizeObserver.observe(container);
78
+ }
79
+ });
80
+ this.destroyRef.onDestroy(() => {
81
+ if (this.rafId)
82
+ cancelAnimationFrame(this.rafId);
83
+ if (this.resizeObserver)
84
+ this.resizeObserver.disconnect();
85
+ });
86
+ }
87
+ clipRangeMatchesSel() {
88
+ const selRange = this.selectionRange();
89
+ const clipRange = this.copyRange() ?? this.cutRange();
90
+ return selRange != null && clipRange != null &&
91
+ selRange.startRow === clipRange.startRow &&
92
+ selRange.startCol === clipRange.startCol &&
93
+ selRange.endRow === clipRange.endRow &&
94
+ selRange.endCol === clipRange.endCol;
95
+ }
96
+ max0(n) {
97
+ return Math.max(0, n);
98
+ }
99
+ };
100
+ MarchingAntsOverlayComponent = __decorate([
101
+ Component({
102
+ selector: 'ogrid-marching-ants-overlay',
103
+ standalone: true,
104
+ imports: [CommonModule],
105
+ template: `
106
+ @if (selRect() && !clipRangeMatchesSel()) {
107
+ <svg
108
+ [style.position]="'absolute'"
109
+ [style.top.px]="selRect()!.top"
110
+ [style.left.px]="selRect()!.left"
111
+ [style.width.px]="selRect()!.width"
112
+ [style.height.px]="selRect()!.height"
113
+ [style.pointer-events]="'none'"
114
+ [style.z-index]="4"
115
+ [style.overflow]="'visible'"
116
+ aria-hidden="true"
117
+ >
118
+ <rect
119
+ x="1" y="1"
120
+ [attr.width]="max0(selRect()!.width - 2)"
121
+ [attr.height]="max0(selRect()!.height - 2)"
122
+ fill="none"
123
+ stroke="var(--ogrid-selection, #217346)"
124
+ stroke-width="2"
125
+ />
126
+ </svg>
127
+ }
128
+ @if (clipRect()) {
129
+ <svg
130
+ [style.position]="'absolute'"
131
+ [style.top.px]="clipRect()!.top"
132
+ [style.left.px]="clipRect()!.left"
133
+ [style.width.px]="clipRect()!.width"
134
+ [style.height.px]="clipRect()!.height"
135
+ [style.pointer-events]="'none'"
136
+ [style.z-index]="5"
137
+ [style.overflow]="'visible'"
138
+ aria-hidden="true"
139
+ >
140
+ <rect
141
+ x="1" y="1"
142
+ [attr.width]="max0(clipRect()!.width - 2)"
143
+ [attr.height]="max0(clipRect()!.height - 2)"
144
+ fill="none"
145
+ stroke="var(--ogrid-selection, #217346)"
146
+ stroke-width="2"
147
+ stroke-dasharray="4 4"
148
+ style="animation: ogrid-marching-ants 0.5s linear infinite"
149
+ />
150
+ </svg>
151
+ }
152
+ `,
153
+ })
154
+ ], MarchingAntsOverlayComponent);
155
+ export { MarchingAntsOverlayComponent };
@@ -0,0 +1,96 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ import { SideBarComponent } from './sidebar.component';
10
+ import { GRID_BORDER_RADIUS } from '@alaarab/ogrid-core';
11
+ let OGridLayoutComponent = class OGridLayoutComponent {
12
+ constructor() {
13
+ this.className = input(undefined);
14
+ this.hasToolbar = input(false);
15
+ this.hasToolbarBelow = input(false);
16
+ this.hasPagination = input(false);
17
+ this.sideBar = input(null);
18
+ this.borderRadius = GRID_BORDER_RADIUS;
19
+ }
20
+ };
21
+ OGridLayoutComponent = __decorate([
22
+ Component({
23
+ selector: 'ogrid-layout',
24
+ standalone: true,
25
+ imports: [CommonModule, SideBarComponent],
26
+ template: `
27
+ <div [class]="className() ?? ''" [style.display]="'flex'" [style.flex-direction]="'column'" [style.height]="'100%'">
28
+ <div [style.border]="'1px solid var(--ogrid-border, #e0e0e0)'"
29
+ [style.border-radius.px]="borderRadius"
30
+ [style.overflow]="'hidden'"
31
+ [style.display]="'flex'"
32
+ [style.flex-direction]="'column'"
33
+ [style.flex]="1"
34
+ [style.min-height]="0"
35
+ [style.background]="'var(--ogrid-bg, #fff)'"
36
+ >
37
+ <!-- Toolbar strip -->
38
+ @if (hasToolbar()) {
39
+ <div
40
+ [style.display]="'flex'"
41
+ [style.justify-content]="'space-between'"
42
+ [style.align-items]="'center'"
43
+ [style.padding]="'6px 12px'"
44
+ [style.background]="'var(--ogrid-header-bg, #f5f5f5)'"
45
+ [style.gap.px]="8"
46
+ [style.flex-wrap]="'wrap'"
47
+ [style.min-height]="0"
48
+ [style.border-bottom]="hasToolbarBelow() ? 'none' : '1px solid var(--ogrid-border, #e0e0e0)'"
49
+ >
50
+ <div style="display:flex;align-items:center;gap:8px">
51
+ <ng-content select="[toolbar]"></ng-content>
52
+ </div>
53
+ <div style="display:flex;align-items:center;gap:8px">
54
+ <ng-content select="[toolbarEnd]"></ng-content>
55
+ </div>
56
+ </div>
57
+ }
58
+
59
+ <!-- Secondary toolbar row -->
60
+ @if (hasToolbarBelow()) {
61
+ <div style="border-bottom:1px solid var(--ogrid-border, #e0e0e0);padding:6px 12px;background:var(--ogrid-header-bg, #f5f5f5)">
62
+ <ng-content select="[toolbarBelow]"></ng-content>
63
+ </div>
64
+ }
65
+
66
+ <!-- Grid area -->
67
+ @if (sideBar()) {
68
+ <div style="width:100%;min-width:0;min-height:0;flex:1;display:flex">
69
+ @if (sideBar()?.position === 'left') {
70
+ <ogrid-sidebar [sideBarProps]="sideBar()"></ogrid-sidebar>
71
+ }
72
+ <div style="flex:1;min-width:0;min-height:0;display:flex;flex-direction:column">
73
+ <ng-content></ng-content>
74
+ </div>
75
+ @if (sideBar()?.position !== 'left') {
76
+ <ogrid-sidebar [sideBarProps]="sideBar()"></ogrid-sidebar>
77
+ }
78
+ </div>
79
+ } @else {
80
+ <div style="width:100%;min-width:0;min-height:0;flex:1;display:flex;flex-direction:column">
81
+ <ng-content></ng-content>
82
+ </div>
83
+ }
84
+
85
+ <!-- Footer strip (pagination) -->
86
+ @if (hasPagination()) {
87
+ <div style="border-top:1px solid var(--ogrid-border, #e0e0e0);background:var(--ogrid-header-bg, #f5f5f5);padding:6px 12px">
88
+ <ng-content select="[pagination]"></ng-content>
89
+ </div>
90
+ }
91
+ </div>
92
+ </div>
93
+ `,
94
+ })
95
+ ], OGridLayoutComponent);
96
+ export { OGridLayoutComponent };
@@ -0,0 +1,229 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ const PANEL_WIDTH = 240;
10
+ const TAB_WIDTH = 36;
11
+ const PANEL_LABELS = { columns: 'Columns', filters: 'Filters' };
12
+ let SideBarComponent = class SideBarComponent {
13
+ constructor() {
14
+ this.sideBarProps = input(null);
15
+ this.panelLabels = PANEL_LABELS;
16
+ this.tabWidth = TAB_WIDTH;
17
+ this.panelWidth = PANEL_WIDTH;
18
+ }
19
+ onTabClick(panel) {
20
+ const props = this.sideBarProps();
21
+ if (props)
22
+ props.onPanelChange(props.activePanel === panel ? null : panel);
23
+ }
24
+ allVisible() {
25
+ const props = this.sideBarProps();
26
+ if (!props)
27
+ return false;
28
+ return props.columns.every((c) => props.visibleColumns.has(c.columnId));
29
+ }
30
+ onSelectAll() {
31
+ const props = this.sideBarProps();
32
+ if (!props)
33
+ return;
34
+ const next = new Set(props.visibleColumns);
35
+ props.columns.forEach((c) => next.add(c.columnId));
36
+ props.onSetVisibleColumns(next);
37
+ }
38
+ onClearAll() {
39
+ const props = this.sideBarProps();
40
+ if (!props)
41
+ return;
42
+ const next = new Set();
43
+ props.columns.forEach((c) => {
44
+ if (c.required && props.visibleColumns.has(c.columnId))
45
+ next.add(c.columnId);
46
+ });
47
+ props.onSetVisibleColumns(next);
48
+ }
49
+ onVisibilityChange(columnKey, visible) {
50
+ this.sideBarProps()?.onVisibilityChange(columnKey, visible);
51
+ }
52
+ getTextFilterValue(filterField) {
53
+ const filters = this.sideBarProps()?.filters;
54
+ const fv = filters?.[filterField];
55
+ return fv?.type === 'text' ? fv.value : '';
56
+ }
57
+ onTextFilterChange(filterField, value) {
58
+ this.sideBarProps()?.onFilterChange(filterField, value ? { type: 'text', value } : undefined);
59
+ }
60
+ getDateFrom(filterField) {
61
+ const fv = this.sideBarProps()?.filters?.[filterField];
62
+ return fv?.type === 'date' ? (fv.value.from ?? '') : '';
63
+ }
64
+ getDateTo(filterField) {
65
+ const fv = this.sideBarProps()?.filters?.[filterField];
66
+ return fv?.type === 'date' ? (fv.value.to ?? '') : '';
67
+ }
68
+ onDateFromChange(filterField, value) {
69
+ const fv = this.sideBarProps()?.filters?.[filterField];
70
+ const existing = fv?.type === 'date' ? fv.value : {};
71
+ const from = value || undefined;
72
+ const to = existing.to;
73
+ this.sideBarProps()?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
74
+ }
75
+ onDateToChange(filterField, value) {
76
+ const fv = this.sideBarProps()?.filters?.[filterField];
77
+ const existing = fv?.type === 'date' ? fv.value : {};
78
+ const to = value || undefined;
79
+ const from = existing.from;
80
+ this.sideBarProps()?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
81
+ }
82
+ getFilterOptions(filterField) {
83
+ return this.sideBarProps()?.filterOptions?.[filterField] ?? [];
84
+ }
85
+ isMultiSelectChecked(filterField, opt) {
86
+ const fv = this.sideBarProps()?.filters?.[filterField];
87
+ return fv?.type === 'multiSelect' ? fv.value.includes(opt) : false;
88
+ }
89
+ onMultiSelectChange(filterField, opt, checked) {
90
+ const fv = this.sideBarProps()?.filters?.[filterField];
91
+ const current = fv?.type === 'multiSelect' ? fv.value : [];
92
+ const next = checked ? [...current, opt] : current.filter((v) => v !== opt);
93
+ this.sideBarProps()?.onFilterChange(filterField, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
94
+ }
95
+ };
96
+ SideBarComponent = __decorate([
97
+ Component({
98
+ selector: 'ogrid-sidebar',
99
+ standalone: true,
100
+ imports: [CommonModule],
101
+ template: `
102
+ <div style="display:flex;flex-direction:row;flex-shrink:0" role="complementary" aria-label="Side bar">
103
+ @if (sideBarProps()?.position === 'left') {
104
+ <ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
105
+ <ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
106
+ }
107
+ @if (sideBarProps()?.position === 'right') {
108
+ <ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
109
+ <ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
110
+ }
111
+ </div>
112
+
113
+ <ng-template #tabStripTpl>
114
+ <div
115
+ [style.display]="'flex'"
116
+ [style.flex-direction]="'column'"
117
+ [style.width.px]="tabWidth"
118
+ [style.background]="'var(--ogrid-header-bg, #f5f5f5)'"
119
+ [style.border-left]="sideBarProps()?.position === 'right' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
120
+ [style.border-right]="sideBarProps()?.position === 'left' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
121
+ role="tablist"
122
+ aria-label="Side bar tabs"
123
+ >
124
+ @for (panel of sideBarProps()?.panels ?? []; track panel) {
125
+ <button
126
+ role="tab"
127
+ [attr.aria-selected]="sideBarProps()?.activePanel === panel"
128
+ [attr.aria-label]="panelLabels[panel]"
129
+ (click)="onTabClick(panel)"
130
+ [title]="panelLabels[panel]"
131
+ [style.width.px]="tabWidth"
132
+ [style.height.px]="tabWidth"
133
+ [style.border]="'none'"
134
+ [style.cursor]="'pointer'"
135
+ [style.color]="'var(--ogrid-fg, #242424)'"
136
+ [style.font-size.px]="14"
137
+ [style.display]="'flex'"
138
+ [style.align-items]="'center'"
139
+ [style.justify-content]="'center'"
140
+ [style.background]="sideBarProps()?.activePanel === panel ? 'var(--ogrid-bg, #fff)' : 'transparent'"
141
+ [style.font-weight]="sideBarProps()?.activePanel === panel ? 'bold' : 'normal'"
142
+ >
143
+ {{ panel === 'columns' ? '\u2261' : '\u2A65' }}
144
+ </button>
145
+ }
146
+ </div>
147
+ </ng-template>
148
+
149
+ <ng-template #panelContentTpl>
150
+ @if (sideBarProps()?.activePanel) {
151
+ <div
152
+ role="tabpanel"
153
+ [attr.aria-label]="panelLabels[sideBarProps()!.activePanel!]"
154
+ [style.width.px]="panelWidth"
155
+ [style.display]="'flex'"
156
+ [style.flex-direction]="'column'"
157
+ [style.overflow]="'hidden'"
158
+ [style.background]="'var(--ogrid-bg, #fff)'"
159
+ [style.color]="'var(--ogrid-fg, #242424)'"
160
+ [style.border-left]="sideBarProps()?.position === 'right' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
161
+ [style.border-right]="sideBarProps()?.position === 'left' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
162
+ >
163
+ <div style="display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid var(--ogrid-border, #e0e0e0);font-weight:600">
164
+ <span>{{ panelLabels[sideBarProps()!.activePanel!] }}</span>
165
+ <button (click)="sideBarProps()?.onPanelChange(null)" style="border:none;background:transparent;cursor:pointer;font-size:16px;color:var(--ogrid-fg, #242424)" aria-label="Close panel">&times;</button>
166
+ </div>
167
+ <div style="flex:1;overflow-y:auto;padding:8px 12px">
168
+ @if (sideBarProps()?.activePanel === 'columns') {
169
+ <div style="display:flex;gap:8px;margin-bottom:8px">
170
+ <button (click)="onSelectAll()" [disabled]="allVisible()" style="flex:1;cursor:pointer;background:var(--ogrid-bg-subtle, #f3f2f1);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;padding:4px 8px">Select All</button>
171
+ <button (click)="onClearAll()" style="flex:1;cursor:pointer;background:var(--ogrid-bg-subtle, #f3f2f1);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;padding:4px 8px">Clear All</button>
172
+ </div>
173
+ @for (col of sideBarProps()?.columns ?? []; track col.columnId) {
174
+ <label style="display:flex;align-items:center;gap:6px;padding:2px 0;cursor:pointer">
175
+ <input type="checkbox" [checked]="sideBarProps()?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, $any($event.target).checked)" [disabled]="col.required" />
176
+ <span>{{ col.name }}</span>
177
+ </label>
178
+ }
179
+ }
180
+ @if (sideBarProps()?.activePanel === 'filters') {
181
+ @if ((sideBarProps()?.filterableColumns ?? []).length === 0) {
182
+ <div style="color:var(--ogrid-muted, #999);font-style:italic">No filterable columns</div>
183
+ }
184
+ @for (col of sideBarProps()?.filterableColumns ?? []; track col.columnId) {
185
+ <div style="margin-bottom:12px">
186
+ <div style="font-weight:500;margin-bottom:4px;font-size:13px">{{ col.name }}</div>
187
+ @if (col.filterType === 'text') {
188
+ <input
189
+ type="text"
190
+ [value]="getTextFilterValue(col.filterField)"
191
+ (input)="onTextFilterChange(col.filterField, $any($event.target).value)"
192
+ [placeholder]="'Filter ' + col.name + '...'"
193
+ [attr.aria-label]="'Filter ' + col.name"
194
+ style="width:100%;box-sizing:border-box;padding:4px 6px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px"
195
+ />
196
+ }
197
+ @if (col.filterType === 'date') {
198
+ <div style="display:flex;flex-direction:column;gap:4px">
199
+ <label style="display:flex;align-items:center;gap:4px;font-size:12px">
200
+ From:
201
+ <input type="date" [value]="getDateFrom(col.filterField)" (change)="onDateFromChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' from date'" style="flex:1;padding:2px 4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px" />
202
+ </label>
203
+ <label style="display:flex;align-items:center;gap:4px;font-size:12px">
204
+ To:
205
+ <input type="date" [value]="getDateTo(col.filterField)" (change)="onDateToChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' to date'" style="flex:1;padding:2px 4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px" />
206
+ </label>
207
+ </div>
208
+ }
209
+ @if (col.filterType === 'multiSelect') {
210
+ <div style="max-height:120px;overflow-y:auto" role="group" [attr.aria-label]="col.name + ' options'">
211
+ @for (opt of getFilterOptions(col.filterField); track opt) {
212
+ <label style="display:flex;align-items:center;gap:4px;padding:1px 0;cursor:pointer;font-size:13px">
213
+ <input type="checkbox" [checked]="isMultiSelectChecked(col.filterField, opt)" (change)="onMultiSelectChange(col.filterField, opt, $any($event.target).checked)" />
214
+ <span>{{ opt }}</span>
215
+ </label>
216
+ }
217
+ </div>
218
+ }
219
+ </div>
220
+ }
221
+ }
222
+ </div>
223
+ </div>
224
+ }
225
+ </ng-template>
226
+ `,
227
+ })
228
+ ], SideBarComponent);
229
+ export { SideBarComponent };