@alaarab/ogrid-angular 2.6.0 → 2.6.1
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/dist/esm/components/base-column-chooser.component.js +77 -0
- package/dist/esm/components/base-column-header-filter.component.js +267 -0
- package/dist/esm/components/base-column-header-menu.component.js +80 -0
- package/dist/esm/components/base-datagrid-table.component.js +768 -0
- package/dist/esm/components/base-inline-cell-editor.component.js +380 -0
- package/dist/esm/components/base-ogrid.component.js +36 -0
- package/dist/esm/components/base-pagination-controls.component.js +68 -0
- package/dist/esm/components/base-popover-cell-editor.component.js +122 -0
- package/dist/esm/components/empty-state.component.js +68 -0
- package/dist/esm/components/formula-bar.component.js +99 -0
- package/dist/esm/components/formula-ref-overlay.component.js +115 -0
- package/dist/esm/components/grid-context-menu.component.js +197 -0
- package/dist/esm/components/inline-cell-editor-template.js +134 -0
- package/dist/esm/components/marching-ants-overlay.component.js +177 -0
- package/dist/esm/components/ogrid-layout.component.js +302 -0
- package/dist/esm/components/sheet-tabs.component.js +83 -0
- package/dist/esm/components/sidebar.component.js +431 -0
- package/dist/esm/components/status-bar.component.js +92 -0
- package/dist/esm/index.js +39 -819
- package/dist/esm/services/column-reorder.service.js +176 -0
- package/dist/esm/services/datagrid-editing.service.js +59 -0
- package/dist/esm/services/datagrid-interaction.service.js +744 -0
- package/dist/esm/services/datagrid-layout.service.js +157 -0
- package/dist/esm/services/datagrid-state.service.js +636 -0
- package/dist/esm/services/formula-engine.service.js +223 -0
- package/dist/esm/services/ogrid.service.js +1094 -0
- package/dist/esm/services/virtual-scroll.service.js +114 -0
- package/dist/esm/styles/ogrid-theme-vars.js +112 -0
- package/dist/esm/types/columnTypes.js +1 -0
- package/dist/esm/types/dataGridTypes.js +1 -0
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/utils/dataGridViewModel.js +6 -0
- package/dist/esm/utils/debounce.js +68 -0
- package/dist/esm/utils/index.js +8 -0
- package/dist/esm/utils/latestRef.js +41 -0
- package/dist/types/components/base-column-chooser.component.d.ts +3 -0
- package/dist/types/components/base-column-header-filter.component.d.ts +3 -0
- package/dist/types/components/base-column-header-menu.component.d.ts +3 -0
- package/dist/types/components/base-datagrid-table.component.d.ts +4 -19
- package/dist/types/components/base-inline-cell-editor.component.d.ts +7 -0
- package/dist/types/components/base-ogrid.component.d.ts +3 -0
- package/dist/types/components/base-pagination-controls.component.d.ts +3 -0
- package/dist/types/components/base-popover-cell-editor.component.d.ts +3 -0
- package/dist/types/components/empty-state.component.d.ts +3 -0
- package/dist/types/components/formula-bar.component.d.ts +3 -8
- package/dist/types/components/formula-ref-overlay.component.d.ts +3 -6
- package/dist/types/components/grid-context-menu.component.d.ts +3 -0
- package/dist/types/components/inline-cell-editor-template.d.ts +2 -2
- package/dist/types/components/marching-ants-overlay.component.d.ts +3 -0
- package/dist/types/components/ogrid-layout.component.d.ts +3 -0
- package/dist/types/components/sheet-tabs.component.d.ts +3 -8
- package/dist/types/components/sidebar.component.d.ts +3 -0
- package/dist/types/components/status-bar.component.d.ts +3 -0
- package/dist/types/index.d.ts +0 -2
- package/dist/types/services/column-reorder.service.d.ts +3 -0
- package/dist/types/services/datagrid-interaction.service.d.ts +1 -0
- package/dist/types/services/datagrid-state.service.d.ts +5 -0
- package/dist/types/services/formula-engine.service.d.ts +3 -9
- package/dist/types/services/ogrid.service.d.ts +8 -11
- package/dist/types/services/virtual-scroll.service.d.ts +3 -0
- package/dist/types/types/dataGridTypes.d.ts +0 -4
- package/package.json +4 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormulaBarComponent -- Standalone Angular formula bar component.
|
|
3
|
+
*
|
|
4
|
+
* Layout: [Name Box] [fx] [Formula Input]
|
|
5
|
+
*
|
|
6
|
+
* Uses --ogrid-* CSS variables for theming.
|
|
7
|
+
* Port of React's FormulaBar component.
|
|
8
|
+
*/
|
|
9
|
+
import { Component, ChangeDetectionStrategy, ViewEncapsulation, input, output, viewChild, effect, } from '@angular/core';
|
|
10
|
+
import { handleFormulaBarKeyDown } from '@alaarab/ogrid-core/formula';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
const _c0 = ["formulaInput"];
|
|
13
|
+
export class FormulaBarComponent {
|
|
14
|
+
constructor() {
|
|
15
|
+
/** Active cell reference (e.g. "A1"). */
|
|
16
|
+
this.cellRef = input(null, ...(ngDevMode ? [{ debugName: "cellRef" }] : []));
|
|
17
|
+
/** Text displayed/edited in the formula input. */
|
|
18
|
+
this.formulaText = input('', ...(ngDevMode ? [{ debugName: "formulaText" }] : []));
|
|
19
|
+
/** Whether the input is in editing mode. */
|
|
20
|
+
this.isEditing = input(false, ...(ngDevMode ? [{ debugName: "isEditing" }] : []));
|
|
21
|
+
/** Called when the user changes the input text. */
|
|
22
|
+
this.inputChange = output();
|
|
23
|
+
/** Commit the formula bar value. */
|
|
24
|
+
this.commit = output();
|
|
25
|
+
/** Cancel editing. */
|
|
26
|
+
this.cancel = output();
|
|
27
|
+
/** Start editing the formula bar. */
|
|
28
|
+
this.startEditing = output();
|
|
29
|
+
this.inputEl = viewChild('formulaInput', ...(ngDevMode ? [{ debugName: "inputEl" }] : []));
|
|
30
|
+
// Focus input when entering edit mode
|
|
31
|
+
effect(() => {
|
|
32
|
+
if (this.isEditing()) {
|
|
33
|
+
const el = this.inputEl()?.nativeElement;
|
|
34
|
+
if (el)
|
|
35
|
+
el.focus();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
onInput(event) {
|
|
40
|
+
this.inputChange.emit(event.target.value);
|
|
41
|
+
}
|
|
42
|
+
onKeyDown(event) {
|
|
43
|
+
handleFormulaBarKeyDown(event.key, () => event.preventDefault(), () => this.commit.emit(), () => this.cancel.emit());
|
|
44
|
+
}
|
|
45
|
+
onClick() {
|
|
46
|
+
if (!this.isEditing()) {
|
|
47
|
+
this.startEditing.emit();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
static { this.ɵfac = function FormulaBarComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FormulaBarComponent)(); }; }
|
|
51
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FormulaBarComponent, selectors: [["ogrid-formula-bar"]], viewQuery: function FormulaBarComponent_Query(rf, ctx) { if (rf & 1) {
|
|
52
|
+
i0.ɵɵviewQuerySignal(ctx.inputEl, _c0, 5);
|
|
53
|
+
} if (rf & 2) {
|
|
54
|
+
i0.ɵɵqueryAdvance();
|
|
55
|
+
} }, inputs: { cellRef: [1, "cellRef"], formulaText: [1, "formulaText"], isEditing: [1, "isEditing"] }, outputs: { inputChange: "inputChange", commit: "commit", cancel: "cancel", startEditing: "startEditing" }, decls: 7, vars: 4, consts: [["formulaInput", ""], ["role", "toolbar", "aria-label", "Formula bar", 1, "ogrid-formula-bar"], ["aria-label", "Active cell reference", 1, "ogrid-formula-bar__name-box"], ["aria-hidden", "true", 1, "ogrid-formula-bar__fx"], ["type", "text", "aria-label", "Formula input", "autocomplete", "off", 1, "ogrid-formula-bar__input", 3, "input", "keydown", "click", "dblclick", "value", "readOnly"]], template: function FormulaBarComponent_Template(rf, ctx) { if (rf & 1) {
|
|
56
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
57
|
+
i0.ɵɵdomElementStart(0, "div", 1)(1, "div", 2);
|
|
58
|
+
i0.ɵɵtext(2);
|
|
59
|
+
i0.ɵɵdomElementEnd();
|
|
60
|
+
i0.ɵɵdomElementStart(3, "div", 3);
|
|
61
|
+
i0.ɵɵtext(4, "fx");
|
|
62
|
+
i0.ɵɵdomElementEnd();
|
|
63
|
+
i0.ɵɵdomElementStart(5, "input", 4, 0);
|
|
64
|
+
i0.ɵɵdomListener("input", function FormulaBarComponent_Template_input_input_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onInput($event)); })("keydown", function FormulaBarComponent_Template_input_keydown_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onKeyDown($event)); })("click", function FormulaBarComponent_Template_input_click_5_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onClick()); })("dblclick", function FormulaBarComponent_Template_input_dblclick_5_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onClick()); });
|
|
65
|
+
i0.ɵɵdomElementEnd()();
|
|
66
|
+
} if (rf & 2) {
|
|
67
|
+
i0.ɵɵadvance(2);
|
|
68
|
+
i0.ɵɵtextInterpolate1(" ", ctx.cellRef() ?? "\u2014", " ");
|
|
69
|
+
i0.ɵɵadvance(3);
|
|
70
|
+
i0.ɵɵdomProperty("value", ctx.formulaText())("readOnly", !ctx.isEditing());
|
|
71
|
+
i0.ɵɵattribute("spellcheck", false);
|
|
72
|
+
} }, styles: ["\n .ogrid-formula-bar {\n display: flex;\n align-items: center;\n border-bottom: 1px solid var(--ogrid-border, #e0e0e0);\n background: var(--ogrid-bg, #fff);\n min-height: 28px;\n font-size: 13px;\n }\n .ogrid-formula-bar__name-box {\n font-family: monospace;\n font-size: 12px;\n font-weight: 500;\n padding: 2px 8px;\n border-right: 1px solid var(--ogrid-border, #e0e0e0);\n background: var(--ogrid-bg, #fff);\n color: var(--ogrid-fg, #242424);\n min-width: 52px;\n text-align: center;\n line-height: 24px;\n user-select: none;\n white-space: nowrap;\n }\n .ogrid-formula-bar__fx {\n padding: 2px 8px;\n font-style: italic;\n font-weight: 600;\n color: var(--ogrid-muted-fg, #888);\n user-select: none;\n border-right: 1px solid var(--ogrid-border, #e0e0e0);\n line-height: 24px;\n font-size: 12px;\n }\n .ogrid-formula-bar__input {\n flex: 1;\n border: none;\n outline: none;\n padding: 2px 8px;\n font-family: monospace;\n font-size: 12px;\n line-height: 24px;\n background: transparent;\n color: var(--ogrid-fg, #242424);\n min-width: 0;\n }\n "], encapsulation: 2, changeDetection: 0 }); }
|
|
73
|
+
}
|
|
74
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FormulaBarComponent, [{
|
|
75
|
+
type: Component,
|
|
76
|
+
args: [{ selector: 'ogrid-formula-bar', standalone: true, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
77
|
+
<div class="ogrid-formula-bar" role="toolbar" aria-label="Formula bar">
|
|
78
|
+
<div class="ogrid-formula-bar__name-box" aria-label="Active cell reference">
|
|
79
|
+
{{ cellRef() ?? '\u2014' }}
|
|
80
|
+
</div>
|
|
81
|
+
<div class="ogrid-formula-bar__fx" aria-hidden="true">fx</div>
|
|
82
|
+
<input
|
|
83
|
+
#formulaInput
|
|
84
|
+
type="text"
|
|
85
|
+
class="ogrid-formula-bar__input"
|
|
86
|
+
[value]="formulaText()"
|
|
87
|
+
[readOnly]="!isEditing()"
|
|
88
|
+
(input)="onInput($event)"
|
|
89
|
+
(keydown)="onKeyDown($event)"
|
|
90
|
+
(click)="onClick()"
|
|
91
|
+
(dblclick)="onClick()"
|
|
92
|
+
aria-label="Formula input"
|
|
93
|
+
[attr.spellcheck]="false"
|
|
94
|
+
autocomplete="off"
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
`, styles: ["\n .ogrid-formula-bar {\n display: flex;\n align-items: center;\n border-bottom: 1px solid var(--ogrid-border, #e0e0e0);\n background: var(--ogrid-bg, #fff);\n min-height: 28px;\n font-size: 13px;\n }\n .ogrid-formula-bar__name-box {\n font-family: monospace;\n font-size: 12px;\n font-weight: 500;\n padding: 2px 8px;\n border-right: 1px solid var(--ogrid-border, #e0e0e0);\n background: var(--ogrid-bg, #fff);\n color: var(--ogrid-fg, #242424);\n min-width: 52px;\n text-align: center;\n line-height: 24px;\n user-select: none;\n white-space: nowrap;\n }\n .ogrid-formula-bar__fx {\n padding: 2px 8px;\n font-style: italic;\n font-weight: 600;\n color: var(--ogrid-muted-fg, #888);\n user-select: none;\n border-right: 1px solid var(--ogrid-border, #e0e0e0);\n line-height: 24px;\n font-size: 12px;\n }\n .ogrid-formula-bar__input {\n flex: 1;\n border: none;\n outline: none;\n padding: 2px 8px;\n font-family: monospace;\n font-size: 12px;\n line-height: 24px;\n background: transparent;\n color: var(--ogrid-fg, #242424);\n min-width: 0;\n }\n "] }]
|
|
98
|
+
}], () => [], { cellRef: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellRef", required: false }] }], formulaText: [{ type: i0.Input, args: [{ isSignal: true, alias: "formulaText", required: false }] }], isEditing: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditing", required: false }] }], inputChange: [{ type: i0.Output, args: ["inputChange"] }], commit: [{ type: i0.Output, args: ["commit"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], startEditing: [{ type: i0.Output, args: ["startEditing"] }], inputEl: [{ type: i0.ViewChild, args: ['formulaInput', { isSignal: true }] }] }); })();
|
|
99
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FormulaBarComponent, { className: "FormulaBarComponent", filePath: "components/formula-bar.component.ts", lineNumber: 96 }); })();
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormulaRefOverlayComponent -- Renders colored border overlays on cells
|
|
3
|
+
* referenced by the active formula, like Excel's reference highlighting.
|
|
4
|
+
*
|
|
5
|
+
* Port of React's FormulaRefOverlay component.
|
|
6
|
+
*/
|
|
7
|
+
import { Component, ChangeDetectionStrategy, ViewEncapsulation, input, signal, effect, } from '@angular/core';
|
|
8
|
+
import { FORMULA_REF_COLORS } from '@alaarab/ogrid-core/formula';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
function FormulaRefOverlayComponent_For_1_Template(rf, ctx) { if (rf & 1) {
|
|
11
|
+
i0.ɵɵnamespaceSVG();
|
|
12
|
+
i0.ɵɵdomElementStart(0, "svg", 1);
|
|
13
|
+
i0.ɵɵdomElement(1, "rect", 2);
|
|
14
|
+
i0.ɵɵdomElementEnd();
|
|
15
|
+
} if (rf & 2) {
|
|
16
|
+
const r_r1 = ctx.$implicit;
|
|
17
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
18
|
+
i0.ɵɵstyleProp("position", "absolute")("top", r_r1.top, "px")("left", r_r1.left, "px")("width", r_r1.width, "px")("height", r_r1.height, "px")("pointer-events", "none")("z-index", 3)("overflow", "visible");
|
|
19
|
+
i0.ɵɵadvance();
|
|
20
|
+
i0.ɵɵattribute("width", ctx_r1.max0(r_r1.width - 2))("height", ctx_r1.max0(r_r1.height - 2))("stroke", r_r1.color);
|
|
21
|
+
} }
|
|
22
|
+
function measureRef(container, ref, colOffset) {
|
|
23
|
+
const startCol = ref.col + colOffset;
|
|
24
|
+
const endCol = (ref.endCol ?? ref.col) + colOffset;
|
|
25
|
+
const endRow = ref.endRow ?? ref.row;
|
|
26
|
+
const tl = container.querySelector(`[data-row-index="${ref.row}"][data-col-index="${startCol}"]`);
|
|
27
|
+
const br = container.querySelector(`[data-row-index="${endRow}"][data-col-index="${endCol}"]`);
|
|
28
|
+
if (!tl || !br)
|
|
29
|
+
return null;
|
|
30
|
+
const cRect = container.getBoundingClientRect();
|
|
31
|
+
const tlRect = tl.getBoundingClientRect();
|
|
32
|
+
const brRect = br.getBoundingClientRect();
|
|
33
|
+
return {
|
|
34
|
+
top: Math.round(tlRect.top - cRect.top),
|
|
35
|
+
left: Math.round(tlRect.left - cRect.left),
|
|
36
|
+
width: Math.round(brRect.right - tlRect.left),
|
|
37
|
+
height: Math.round(brRect.bottom - tlRect.top),
|
|
38
|
+
color: FORMULA_REF_COLORS[ref.colorIndex % FORMULA_REF_COLORS.length],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export class FormulaRefOverlayComponent {
|
|
42
|
+
constructor() {
|
|
43
|
+
/** The positioned container that wraps the table. */
|
|
44
|
+
this.containerEl = input(null, ...(ngDevMode ? [{ debugName: "containerEl" }] : []));
|
|
45
|
+
/** References to highlight. */
|
|
46
|
+
this.references = input([], ...(ngDevMode ? [{ debugName: "references" }] : []));
|
|
47
|
+
/** Column offset (1 when checkbox/row-number columns are present). */
|
|
48
|
+
this.colOffset = input(0, ...(ngDevMode ? [{ debugName: "colOffset" }] : []));
|
|
49
|
+
this.rects = signal([], ...(ngDevMode ? [{ debugName: "rects" }] : []));
|
|
50
|
+
this.rafId = 0;
|
|
51
|
+
effect(() => {
|
|
52
|
+
const refs = this.references();
|
|
53
|
+
const container = this.containerEl();
|
|
54
|
+
const colOff = this.colOffset();
|
|
55
|
+
cancelAnimationFrame(this.rafId);
|
|
56
|
+
if (!container || refs.length === 0) {
|
|
57
|
+
this.rects.set([]);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.rafId = requestAnimationFrame(() => {
|
|
61
|
+
const measured = [];
|
|
62
|
+
for (const ref of refs) {
|
|
63
|
+
const r = measureRef(container, ref, colOff);
|
|
64
|
+
if (r)
|
|
65
|
+
measured.push(r);
|
|
66
|
+
}
|
|
67
|
+
this.rects.set(measured);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
max0(v) {
|
|
72
|
+
return Math.max(0, v);
|
|
73
|
+
}
|
|
74
|
+
static { this.ɵfac = function FormulaRefOverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FormulaRefOverlayComponent)(); }; }
|
|
75
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FormulaRefOverlayComponent, selectors: [["ogrid-formula-ref-overlay"]], inputs: { containerEl: [1, "containerEl"], references: [1, "references"], colOffset: [1, "colOffset"] }, decls: 2, vars: 0, consts: [["aria-hidden", "true", 3, "position", "top", "left", "width", "height", "pointerEvents", "zIndex", "overflow"], ["aria-hidden", "true"], ["x", "1", "y", "1", "fill", "none", "stroke-width", "2", 2, "shape-rendering", "crispEdges"]], template: function FormulaRefOverlayComponent_Template(rf, ctx) { if (rf & 1) {
|
|
76
|
+
i0.ɵɵrepeaterCreate(0, FormulaRefOverlayComponent_For_1_Template, 2, 19, ":svg:svg", 0, i0.ɵɵrepeaterTrackByIndex);
|
|
77
|
+
} if (rf & 2) {
|
|
78
|
+
i0.ɵɵrepeater(ctx.rects());
|
|
79
|
+
} }, encapsulation: 2, changeDetection: 0 }); }
|
|
80
|
+
}
|
|
81
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FormulaRefOverlayComponent, [{
|
|
82
|
+
type: Component,
|
|
83
|
+
args: [{
|
|
84
|
+
selector: 'ogrid-formula-ref-overlay',
|
|
85
|
+
standalone: true,
|
|
86
|
+
encapsulation: ViewEncapsulation.None,
|
|
87
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
88
|
+
template: `
|
|
89
|
+
@for (r of rects(); track $index) {
|
|
90
|
+
<svg
|
|
91
|
+
[style.position]="'absolute'"
|
|
92
|
+
[style.top.px]="r.top"
|
|
93
|
+
[style.left.px]="r.left"
|
|
94
|
+
[style.width.px]="r.width"
|
|
95
|
+
[style.height.px]="r.height"
|
|
96
|
+
[style.pointerEvents]="'none'"
|
|
97
|
+
[style.zIndex]="3"
|
|
98
|
+
[style.overflow]="'visible'"
|
|
99
|
+
aria-hidden="true"
|
|
100
|
+
>
|
|
101
|
+
<rect
|
|
102
|
+
x="1" y="1"
|
|
103
|
+
[attr.width]="max0(r.width - 2)"
|
|
104
|
+
[attr.height]="max0(r.height - 2)"
|
|
105
|
+
fill="none"
|
|
106
|
+
[attr.stroke]="r.color"
|
|
107
|
+
stroke-width="2"
|
|
108
|
+
style="shape-rendering: crispEdges"
|
|
109
|
+
/>
|
|
110
|
+
</svg>
|
|
111
|
+
}
|
|
112
|
+
`,
|
|
113
|
+
}]
|
|
114
|
+
}], () => [], { containerEl: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerEl", required: false }] }], references: [{ type: i0.Input, args: [{ isSignal: true, alias: "references", required: false }] }], colOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "colOffset", required: false }] }] }); })();
|
|
115
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FormulaRefOverlayComponent, { className: "FormulaRefOverlayComponent", filePath: "components/formula-ref-overlay.component.ts", lineNumber: 88 }); })();
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ViewChild, DestroyRef, inject, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import { GRID_CONTEXT_MENU_ITEMS, formatShortcut } from '@alaarab/ogrid-core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
const _c0 = ["menuRef"];
|
|
5
|
+
const _forTrack0 = ($index, $item) => $item.id;
|
|
6
|
+
function GridContextMenuComponent_For_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
7
|
+
i0.ɵɵdomElement(0, "div");
|
|
8
|
+
} if (rf & 2) {
|
|
9
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
10
|
+
i0.ɵɵclassMap((ctx_r1.classNames == null ? null : ctx_r1.classNames.contextMenuDivider) ?? "");
|
|
11
|
+
} }
|
|
12
|
+
function GridContextMenuComponent_For_3_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
13
|
+
i0.ɵɵdomElementStart(0, "span");
|
|
14
|
+
i0.ɵɵtext(1);
|
|
15
|
+
i0.ɵɵdomElementEnd();
|
|
16
|
+
} if (rf & 2) {
|
|
17
|
+
const item_r3 = i0.ɵɵnextContext().$implicit;
|
|
18
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
19
|
+
i0.ɵɵclassMap((ctx_r1.classNames == null ? null : ctx_r1.classNames.contextMenuItemShortcut) ?? "");
|
|
20
|
+
i0.ɵɵadvance();
|
|
21
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.formatShortcutFn(item_r3.shortcut), " ");
|
|
22
|
+
} }
|
|
23
|
+
function GridContextMenuComponent_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
25
|
+
i0.ɵɵconditionalCreate(0, GridContextMenuComponent_For_3_Conditional_0_Template, 1, 2, "div", 2);
|
|
26
|
+
i0.ɵɵdomElementStart(1, "button", 3);
|
|
27
|
+
i0.ɵɵdomListener("click", function GridContextMenuComponent_For_3_Template_button_click_1_listener() { const item_r3 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onItemClick(item_r3.id)); });
|
|
28
|
+
i0.ɵɵdomElementStart(2, "span");
|
|
29
|
+
i0.ɵɵtext(3);
|
|
30
|
+
i0.ɵɵdomElementEnd();
|
|
31
|
+
i0.ɵɵconditionalCreate(4, GridContextMenuComponent_For_3_Conditional_4_Template, 2, 3, "span", 2);
|
|
32
|
+
i0.ɵɵdomElementEnd();
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const item_r3 = ctx.$implicit;
|
|
35
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
36
|
+
i0.ɵɵconditional(item_r3.dividerBefore ? 0 : -1);
|
|
37
|
+
i0.ɵɵadvance();
|
|
38
|
+
i0.ɵɵclassMap((ctx_r1.classNames == null ? null : ctx_r1.classNames.contextMenuItem) ?? "");
|
|
39
|
+
i0.ɵɵdomProperty("disabled", ctx_r1.isDisabled(item_r3));
|
|
40
|
+
i0.ɵɵadvance();
|
|
41
|
+
i0.ɵɵclassMap((ctx_r1.classNames == null ? null : ctx_r1.classNames.contextMenuItemLabel) ?? "");
|
|
42
|
+
i0.ɵɵadvance();
|
|
43
|
+
i0.ɵɵtextInterpolate(item_r3.label);
|
|
44
|
+
i0.ɵɵadvance();
|
|
45
|
+
i0.ɵɵconditional(item_r3.shortcut ? 4 : -1);
|
|
46
|
+
} }
|
|
47
|
+
export class GridContextMenuComponent {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.destroyRef = inject(DestroyRef);
|
|
50
|
+
this.hasSelection = false;
|
|
51
|
+
this.canUndoProp = false;
|
|
52
|
+
this.canRedoProp = false;
|
|
53
|
+
this.classNames = undefined;
|
|
54
|
+
this.copyAction = new EventEmitter();
|
|
55
|
+
this.cutAction = new EventEmitter();
|
|
56
|
+
this.pasteAction = new EventEmitter();
|
|
57
|
+
this.selectAllAction = new EventEmitter();
|
|
58
|
+
this.undoAction = new EventEmitter();
|
|
59
|
+
this.redoAction = new EventEmitter();
|
|
60
|
+
this.closeAction = new EventEmitter();
|
|
61
|
+
this.menuItems = GRID_CONTEXT_MENU_ITEMS;
|
|
62
|
+
this.formatShortcutFn = formatShortcut;
|
|
63
|
+
this.clickOutsideHandler = (e) => {
|
|
64
|
+
const el = this.menuRef?.nativeElement;
|
|
65
|
+
if (el && !el.contains(e.target))
|
|
66
|
+
this.closeAction.emit();
|
|
67
|
+
};
|
|
68
|
+
this.keyDownHandler = (e) => {
|
|
69
|
+
if (e.key === 'Escape')
|
|
70
|
+
this.closeAction.emit();
|
|
71
|
+
};
|
|
72
|
+
// Register listeners once on init (no signal dependencies needed)
|
|
73
|
+
document.addEventListener('mousedown', this.clickOutsideHandler, true);
|
|
74
|
+
document.addEventListener('keydown', this.keyDownHandler, true);
|
|
75
|
+
this.destroyRef.onDestroy(() => {
|
|
76
|
+
document.removeEventListener('mousedown', this.clickOutsideHandler, true);
|
|
77
|
+
document.removeEventListener('keydown', this.keyDownHandler, true);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
isDisabled(item) {
|
|
81
|
+
if (item.disabledWhenNoSelection && !this.hasSelection)
|
|
82
|
+
return true;
|
|
83
|
+
if (item.id === 'undo' && !this.canUndoProp)
|
|
84
|
+
return true;
|
|
85
|
+
if (item.id === 'redo' && !this.canRedoProp)
|
|
86
|
+
return true;
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
onItemClick(id) {
|
|
90
|
+
switch (id) {
|
|
91
|
+
case 'copy':
|
|
92
|
+
this.copyAction.emit();
|
|
93
|
+
break;
|
|
94
|
+
case 'cut':
|
|
95
|
+
this.cutAction.emit();
|
|
96
|
+
break;
|
|
97
|
+
case 'paste':
|
|
98
|
+
this.pasteAction.emit();
|
|
99
|
+
break;
|
|
100
|
+
case 'selectAll':
|
|
101
|
+
this.selectAllAction.emit();
|
|
102
|
+
break;
|
|
103
|
+
case 'undo':
|
|
104
|
+
this.undoAction.emit();
|
|
105
|
+
break;
|
|
106
|
+
case 'redo':
|
|
107
|
+
this.redoAction.emit();
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
this.closeAction.emit();
|
|
111
|
+
}
|
|
112
|
+
static { this.ɵfac = function GridContextMenuComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || GridContextMenuComponent)(); }; }
|
|
113
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: GridContextMenuComponent, selectors: [["ogrid-context-menu"]], viewQuery: function GridContextMenuComponent_Query(rf, ctx) { if (rf & 1) {
|
|
114
|
+
i0.ɵɵviewQuery(_c0, 5);
|
|
115
|
+
} if (rf & 2) {
|
|
116
|
+
let _t;
|
|
117
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.menuRef = _t.first);
|
|
118
|
+
} }, inputs: { x: "x", y: "y", hasSelection: "hasSelection", canUndoProp: "canUndoProp", canRedoProp: "canRedoProp", classNames: "classNames" }, outputs: { copyAction: "copyAction", cutAction: "cutAction", pasteAction: "pasteAction", selectAllAction: "selectAllAction", undoAction: "undoAction", redoAction: "redoAction", closeAction: "closeAction" }, decls: 4, vars: 6, consts: [["menuRef", ""], ["role", "menu", "aria-label", "Grid context menu"], [3, "class"], ["type", "button", 3, "click", "disabled"]], template: function GridContextMenuComponent_Template(rf, ctx) { if (rf & 1) {
|
|
119
|
+
i0.ɵɵdomElementStart(0, "div", 1, 0);
|
|
120
|
+
i0.ɵɵrepeaterCreate(2, GridContextMenuComponent_For_3_Template, 5, 8, null, null, _forTrack0);
|
|
121
|
+
i0.ɵɵdomElementEnd();
|
|
122
|
+
} if (rf & 2) {
|
|
123
|
+
i0.ɵɵclassMap((ctx.classNames == null ? null : ctx.classNames.contextMenu) ?? "");
|
|
124
|
+
i0.ɵɵstyleProp("left", ctx.x, "px")("top", ctx.y, "px");
|
|
125
|
+
i0.ɵɵadvance(2);
|
|
126
|
+
i0.ɵɵrepeater(ctx.menuItems);
|
|
127
|
+
} }, encapsulation: 2, changeDetection: 0 }); }
|
|
128
|
+
}
|
|
129
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(GridContextMenuComponent, [{
|
|
130
|
+
type: Component,
|
|
131
|
+
args: [{
|
|
132
|
+
selector: 'ogrid-context-menu',
|
|
133
|
+
standalone: true,
|
|
134
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
135
|
+
template: `
|
|
136
|
+
<div
|
|
137
|
+
#menuRef
|
|
138
|
+
[class]="classNames?.contextMenu ?? ''"
|
|
139
|
+
role="menu"
|
|
140
|
+
[style.left.px]="x"
|
|
141
|
+
[style.top.px]="y"
|
|
142
|
+
aria-label="Grid context menu"
|
|
143
|
+
>
|
|
144
|
+
@for (item of menuItems; track item.id) {
|
|
145
|
+
@if (item.dividerBefore) {
|
|
146
|
+
<div [class]="classNames?.contextMenuDivider ?? ''"></div>
|
|
147
|
+
}
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
[class]="classNames?.contextMenuItem ?? ''"
|
|
151
|
+
(click)="onItemClick(item.id)"
|
|
152
|
+
[disabled]="isDisabled(item)"
|
|
153
|
+
>
|
|
154
|
+
<span [class]="classNames?.contextMenuItemLabel ?? ''">{{ item.label }}</span>
|
|
155
|
+
@if (item.shortcut) {
|
|
156
|
+
<span [class]="classNames?.contextMenuItemShortcut ?? ''">
|
|
157
|
+
{{ formatShortcutFn(item.shortcut) }}
|
|
158
|
+
</span>
|
|
159
|
+
}
|
|
160
|
+
</button>
|
|
161
|
+
}
|
|
162
|
+
</div>
|
|
163
|
+
`,
|
|
164
|
+
}]
|
|
165
|
+
}], () => [], { x: [{
|
|
166
|
+
type: Input,
|
|
167
|
+
args: [{ required: true }]
|
|
168
|
+
}], y: [{
|
|
169
|
+
type: Input,
|
|
170
|
+
args: [{ required: true }]
|
|
171
|
+
}], hasSelection: [{
|
|
172
|
+
type: Input
|
|
173
|
+
}], canUndoProp: [{
|
|
174
|
+
type: Input
|
|
175
|
+
}], canRedoProp: [{
|
|
176
|
+
type: Input
|
|
177
|
+
}], classNames: [{
|
|
178
|
+
type: Input
|
|
179
|
+
}], copyAction: [{
|
|
180
|
+
type: Output
|
|
181
|
+
}], cutAction: [{
|
|
182
|
+
type: Output
|
|
183
|
+
}], pasteAction: [{
|
|
184
|
+
type: Output
|
|
185
|
+
}], selectAllAction: [{
|
|
186
|
+
type: Output
|
|
187
|
+
}], undoAction: [{
|
|
188
|
+
type: Output
|
|
189
|
+
}], redoAction: [{
|
|
190
|
+
type: Output
|
|
191
|
+
}], closeAction: [{
|
|
192
|
+
type: Output
|
|
193
|
+
}], menuRef: [{
|
|
194
|
+
type: ViewChild,
|
|
195
|
+
args: ['menuRef']
|
|
196
|
+
}] }); })();
|
|
197
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GridContextMenuComponent, { className: "GridContextMenuComponent", filePath: "components/grid-context-menu.component.ts", lineNumber: 38 }); })();
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared inline cell editor template used by all Angular UI packages.
|
|
3
|
+
* The template is identical across Material, PrimeNG, and Radix implementations.
|
|
4
|
+
*/
|
|
5
|
+
export const INLINE_CELL_EDITOR_TEMPLATE = `
|
|
6
|
+
@switch (editorType) {
|
|
7
|
+
@case ('text') {
|
|
8
|
+
<input
|
|
9
|
+
#inputEl
|
|
10
|
+
type="text"
|
|
11
|
+
[value]="localValue()"
|
|
12
|
+
(input)="localValue.set($any($event.target).value)"
|
|
13
|
+
(keydown)="onTextKeyDown($event)"
|
|
14
|
+
(blur)="onTextBlur()"
|
|
15
|
+
[style]="getInputStyle()"
|
|
16
|
+
/>
|
|
17
|
+
}
|
|
18
|
+
@case ('richSelect') {
|
|
19
|
+
<div #richSelectWrapper
|
|
20
|
+
style="width:100%;height:100%;display:flex;align-items:center;box-sizing:border-box;overflow:hidden;min-width:0;position:relative">
|
|
21
|
+
<div style="display:flex;align-items:center;justify-content:space-between;width:100%;min-width:0;cursor:pointer;font-size:var(--ogrid-cell-font-size, 13px);color:inherit">
|
|
22
|
+
<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ getDisplayText(value) }}</span>
|
|
23
|
+
<span style="margin-left:4px;font-size:10px;opacity:0.5">▾</span>
|
|
24
|
+
</div>
|
|
25
|
+
<div #richSelectDropdown role="listbox"
|
|
26
|
+
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2);text-align:left;font-size:var(--ogrid-cell-font-size, 13px);font-family:inherit">
|
|
27
|
+
<input
|
|
28
|
+
#richSelectInput
|
|
29
|
+
type="text"
|
|
30
|
+
[value]="searchText()"
|
|
31
|
+
(input)="onRichSelectSearch($any($event.target).value)"
|
|
32
|
+
(keydown)="onRichSelectKeyDown($event)"
|
|
33
|
+
placeholder="Search..."
|
|
34
|
+
style="width:100%;padding:6px 8px;border:none;border-bottom:1px solid var(--ogrid-border, rgba(0,0,0,0.12));background:var(--ogrid-bg, #fff);color:inherit;font:inherit;font-size:var(--ogrid-cell-font-size, 13px);outline:none;box-sizing:border-box;position:sticky;top:0;z-index:1"
|
|
35
|
+
/>
|
|
36
|
+
<div #richSelectOptions>
|
|
37
|
+
@for (opt of filteredOptions(); track opt; let i = $index) {
|
|
38
|
+
<div role="option"
|
|
39
|
+
[attr.aria-selected]="i === highlightedIndex()"
|
|
40
|
+
(click)="commitValue(opt)"
|
|
41
|
+
[style]="i === highlightedIndex() ? 'padding:6px 8px;cursor:pointer;color:var(--ogrid-fg, #242424);font-size:var(--ogrid-cell-font-size, 13px);background:var(--ogrid-bg-hover, #e8f0fe)' : 'padding:6px 8px;cursor:pointer;color:var(--ogrid-fg, #242424);font-size:var(--ogrid-cell-font-size, 13px)'">
|
|
42
|
+
{{ getDisplayText(opt) }}
|
|
43
|
+
</div>
|
|
44
|
+
}
|
|
45
|
+
@if (filteredOptions().length === 0) {
|
|
46
|
+
<div style="padding:6px 8px;color:var(--ogrid-muted, #999);font-size:var(--ogrid-cell-font-size, 13px)">No matches</div>
|
|
47
|
+
}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
}
|
|
52
|
+
@case ('select') {
|
|
53
|
+
<div #selectWrapper tabindex="0"
|
|
54
|
+
style="width:100%;height:100%;display:flex;align-items:center;box-sizing:border-box;overflow:hidden;min-width:0;position:relative"
|
|
55
|
+
(keydown)="onCustomSelectKeyDown($event)">
|
|
56
|
+
<div style="display:flex;align-items:center;justify-content:space-between;width:100%;min-width:0;cursor:pointer;font-size:var(--ogrid-cell-font-size, 13px);color:inherit">
|
|
57
|
+
<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ getDisplayText(value) }}</span>
|
|
58
|
+
<span style="margin-left:4px;font-size:10px;opacity:0.5">▾</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div #selectDropdown role="listbox"
|
|
61
|
+
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2);text-align:left;font-size:var(--ogrid-cell-font-size, 13px);font-family:inherit">
|
|
62
|
+
@for (opt of selectOptions(); track opt; let i = $index) {
|
|
63
|
+
<div role="option"
|
|
64
|
+
[attr.aria-selected]="i === highlightedIndex()"
|
|
65
|
+
(click)="commitValue(opt)"
|
|
66
|
+
[style]="i === highlightedIndex() ? 'padding:6px 8px;cursor:pointer;color:var(--ogrid-fg, #242424);font-size:var(--ogrid-cell-font-size, 13px);background:var(--ogrid-bg-hover, #e8f0fe)' : 'padding:6px 8px;cursor:pointer;color:var(--ogrid-fg, #242424);font-size:var(--ogrid-cell-font-size, 13px)'">
|
|
67
|
+
{{ getDisplayText(opt) }}
|
|
68
|
+
</div>
|
|
69
|
+
}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
}
|
|
73
|
+
@case ('checkbox') {
|
|
74
|
+
<div style="display:flex;align-items:center;justify-content:center;width:100%;height:100%">
|
|
75
|
+
<input
|
|
76
|
+
type="checkbox"
|
|
77
|
+
[checked]="!!localValue()"
|
|
78
|
+
(change)="commitValue($any($event.target).checked)"
|
|
79
|
+
(keydown)="onCheckboxKeyDown($event)"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
}
|
|
83
|
+
@case ('date') {
|
|
84
|
+
@if (getCellEditorType() === 'native') {
|
|
85
|
+
<input
|
|
86
|
+
#inputEl
|
|
87
|
+
type="date"
|
|
88
|
+
[value]="localValue()"
|
|
89
|
+
(input)="localValue.set($any($event.target).value)"
|
|
90
|
+
(keydown)="onTextKeyDown($event)"
|
|
91
|
+
(blur)="onTextBlur()"
|
|
92
|
+
[style]="getInputStyle()"
|
|
93
|
+
/>
|
|
94
|
+
} @else {
|
|
95
|
+
<input
|
|
96
|
+
#inputEl
|
|
97
|
+
type="text"
|
|
98
|
+
[placeholder]="getDatePlaceholder()"
|
|
99
|
+
[value]="localValue()"
|
|
100
|
+
(input)="localValue.set($any($event.target).value)"
|
|
101
|
+
(keydown)="onTextKeyDown($event)"
|
|
102
|
+
(blur)="onTextBlur()"
|
|
103
|
+
[style]="getInputStyle()"
|
|
104
|
+
/>
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
@default {
|
|
108
|
+
<input
|
|
109
|
+
#inputEl
|
|
110
|
+
type="text"
|
|
111
|
+
[value]="localValue()"
|
|
112
|
+
(input)="localValue.set($any($event.target).value)"
|
|
113
|
+
(keydown)="onTextKeyDown($event)"
|
|
114
|
+
(blur)="onTextBlur()"
|
|
115
|
+
[style]="getInputStyle()"
|
|
116
|
+
/>
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
export const INLINE_CELL_EDITOR_STYLES = `
|
|
121
|
+
:host {
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
width: 100%;
|
|
125
|
+
height: 100%;
|
|
126
|
+
min-width: 0;
|
|
127
|
+
padding-top: var(--ogrid-cell-padding-vertical, 6px);
|
|
128
|
+
padding-bottom: var(--ogrid-cell-padding-vertical, 6px);
|
|
129
|
+
padding-left: var(--ogrid-cell-padding-horizontal, 10px);
|
|
130
|
+
padding-right: var(--ogrid-cell-padding-horizontal, 10px);
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
}
|
|
134
|
+
`;
|