angular-my-report 0.1.0 → 0.1.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/README.md +124 -40
- package/fesm2022/angular-my-report.mjs +7931 -0
- package/fesm2022/angular-my-report.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/engine/barcode.d.ts +17 -0
- package/lib/engine/chart.d.ts +26 -0
- package/lib/engine/chartLayout.d.ts +45 -0
- package/lib/engine/crosstab.d.ts +22 -0
- package/lib/engine/dataSources.d.ts +35 -0
- package/lib/engine/expressions.d.ts +46 -0
- package/lib/engine/measure.d.ts +7 -0
- package/lib/engine/parameters.d.ts +15 -0
- package/lib/engine/qr.d.ts +8 -0
- package/lib/engine/renderer.d.ts +43 -0
- package/lib/engine/table.d.ts +22 -0
- package/lib/model/defaults.d.ts +31 -0
- package/lib/model/factories.d.ts +31 -0
- package/lib/model/types.d.ts +244 -0
- package/lib/ui/css.d.ts +2 -0
- package/lib/ui/designer/banded-table-dialog.component.d.ts +27 -0
- package/lib/ui/designer/canvas.component.d.ts +41 -0
- package/lib/ui/designer/data-source-dialog.component.d.ts +37 -0
- package/lib/ui/designer/element-box.component.d.ts +32 -0
- package/lib/ui/designer/graphics.components.d.ts +36 -0
- package/lib/ui/designer/left-panel.component.d.ts +37 -0
- package/lib/ui/designer/safe-image.pipe.d.ts +13 -0
- package/lib/ui/designer/table-view.component.d.ts +19 -0
- package/lib/ui/designer.component.d.ts +59 -0
- package/lib/ui/designer.store.d.ts +82 -0
- package/lib/ui/inputs.d.ts +8 -0
- package/lib/ui/properties/band-props.component.d.ts +15 -0
- package/lib/ui/properties/element-props.component.d.ts +46 -0
- package/lib/ui/properties/expr-input.component.d.ts +43 -0
- package/lib/ui/properties/graphics-props.component.d.ts +52 -0
- package/lib/ui/properties/image-picker.component.d.ts +15 -0
- package/lib/ui/properties/properties-panel.component.d.ts +13 -0
- package/lib/ui/properties/report-extras.component.d.ts +40 -0
- package/lib/ui/properties/report-props.component.d.ts +34 -0
- package/lib/ui/properties/table-props.component.d.ts +31 -0
- package/lib/ui/viewer/page-view.component.d.ts +17 -0
- package/lib/ui/viewer/render.d.ts +43 -0
- package/lib/ui/viewer/viewer.component.d.ts +99 -0
- package/lib/ui/viewer.component.d.ts +40 -0
- package/package.json +16 -5
- package/{src/public-api.ts → public-api.d.ts} +0 -5
- package/ng-package.json +0 -7
- package/src/lib/engine/barcode.ts +0 -118
- package/src/lib/engine/chart.ts +0 -85
- package/src/lib/engine/chartLayout.ts +0 -265
- package/src/lib/engine/crosstab.ts +0 -96
- package/src/lib/engine/dataSources.ts +0 -204
- package/src/lib/engine/expressions.ts +0 -734
- package/src/lib/engine/measure.ts +0 -62
- package/src/lib/engine/parameters.ts +0 -96
- package/src/lib/engine/qr.ts +0 -26
- package/src/lib/engine/renderer.ts +0 -324
- package/src/lib/engine/table.ts +0 -58
- package/src/lib/model/defaults.ts +0 -249
- package/src/lib/model/factories.ts +0 -324
- package/src/lib/model/types.ts +0 -281
- package/src/lib/ui/css.ts +0 -12
- package/src/lib/ui/designer/banded-table-dialog.component.ts +0 -127
- package/src/lib/ui/designer/canvas.component.ts +0 -281
- package/src/lib/ui/designer/data-source-dialog.component.ts +0 -201
- package/src/lib/ui/designer/element-box.component.ts +0 -138
- package/src/lib/ui/designer/graphics.components.ts +0 -140
- package/src/lib/ui/designer/left-panel.component.ts +0 -189
- package/src/lib/ui/designer/safe-image.pipe.ts +0 -16
- package/src/lib/ui/designer/table-view.component.ts +0 -120
- package/src/lib/ui/designer.component.ts +0 -296
- package/src/lib/ui/designer.store.ts +0 -230
- package/src/lib/ui/inputs.ts +0 -34
- package/src/lib/ui/myreport.css +0 -267
- package/src/lib/ui/properties/band-props.component.ts +0 -86
- package/src/lib/ui/properties/element-props.component.ts +0 -326
- package/src/lib/ui/properties/expr-input.component.ts +0 -191
- package/src/lib/ui/properties/graphics-props.component.ts +0 -260
- package/src/lib/ui/properties/image-picker.component.ts +0 -62
- package/src/lib/ui/properties/properties-panel.component.ts +0 -37
- package/src/lib/ui/properties/report-extras.component.ts +0 -198
- package/src/lib/ui/properties/report-props.component.ts +0 -211
- package/src/lib/ui/properties/table-props.component.ts +0 -197
- package/src/lib/ui/viewer/page-view.component.ts +0 -73
- package/src/lib/ui/viewer/render.ts +0 -161
- package/src/lib/ui/viewer/viewer.component.ts +0 -555
- package/src/lib/ui/viewer.component.ts +0 -107
- package/tsconfig.lib.json +0 -12
- package/tsconfig.lib.prod.json +0 -5
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BandType, PageSettings, ReportDefinition } from '../../model/types';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare function removeGroup(r: ReportDefinition, gid: string): void;
|
|
4
|
+
type PageKey = Exclude<keyof PageSettings, 'continuous'>;
|
|
5
|
+
export declare class ReportPropsComponent {
|
|
6
|
+
private readonly store;
|
|
7
|
+
protected readonly report: import("@angular/core").WritableSignal<ReportDefinition>;
|
|
8
|
+
protected readonly labels: Record<BandType, string>;
|
|
9
|
+
protected readonly optionalBands: BandType[];
|
|
10
|
+
protected readonly sizeNames: string[];
|
|
11
|
+
protected readonly pageFields: {
|
|
12
|
+
key: PageKey;
|
|
13
|
+
label: string;
|
|
14
|
+
min: number;
|
|
15
|
+
}[];
|
|
16
|
+
protected readonly landscape: import("@angular/core").Signal<boolean>;
|
|
17
|
+
protected readonly sizeName: import("@angular/core").Signal<string>;
|
|
18
|
+
protected setName(v: string): void;
|
|
19
|
+
protected setSize(name: string): void;
|
|
20
|
+
protected setContinuous(on: boolean): void;
|
|
21
|
+
protected setOrientation(v: string): void;
|
|
22
|
+
protected setPage(k: PageKey, min: number, v: number | null): void;
|
|
23
|
+
protected isVisible(t: BandType): boolean;
|
|
24
|
+
protected setVisible(t: BandType, on: boolean): void;
|
|
25
|
+
protected setGroupField(gid: string, v: string): void;
|
|
26
|
+
protected deleteGroup(gid: string): void;
|
|
27
|
+
protected addGroup(): void;
|
|
28
|
+
protected setSort(i: number, k: 'field' | 'direction', v: string): void;
|
|
29
|
+
protected removeSort(i: number): void;
|
|
30
|
+
protected addSort(): void;
|
|
31
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ReportPropsComponent, never>;
|
|
32
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ReportPropsComponent, "rpt-report-props", never, {}, {}, never, never, true, never>;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FormatKind, HAlign, ReportElement, TableColumn, TableSpec } from '../../model/types';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
type ColorKey = 'headerBackground' | 'stripeBackground' | 'gridColor' | 'headerColor';
|
|
4
|
+
export declare class TablePropsComponent {
|
|
5
|
+
private readonly store;
|
|
6
|
+
readonly el: import("@angular/core").InputSignal<ReportElement>;
|
|
7
|
+
protected readonly formatKinds: FormatKind[];
|
|
8
|
+
protected readonly spec: import("@angular/core").Signal<TableSpec>;
|
|
9
|
+
protected readonly widths: import("@angular/core").Signal<number[]>;
|
|
10
|
+
/** Edits the table and keeps the element height in sync with its header/row/footer heights. */
|
|
11
|
+
protected set(fn: (t: TableSpec, e: ReportElement) => void): void;
|
|
12
|
+
protected col(i: number, fn: (c: TableColumn) => void): void;
|
|
13
|
+
protected colorValue(v: string): string;
|
|
14
|
+
protected flag: (k: "showHeader" | "repeatHeader" | "showFooter", v: boolean) => (t: TableSpec) => boolean;
|
|
15
|
+
protected color: (k: ColorKey, v: string) => (t: TableSpec) => string;
|
|
16
|
+
protected rowFill: (v: string) => (_: TableSpec, e: ReportElement) => string;
|
|
17
|
+
protected setHeader: (v: string) => (c: TableColumn) => string;
|
|
18
|
+
protected setCell: (v: string) => (c: TableColumn) => string;
|
|
19
|
+
protected setFooter: (v: string) => (c: TableColumn) => string;
|
|
20
|
+
protected setAlign: (v: HAlign) => (c: TableColumn) => HAlign;
|
|
21
|
+
protected setFormat: (v: FormatKind) => (c: TableColumn) => FormatKind;
|
|
22
|
+
protected height(k: 'headerHeight' | 'rowHeight', v: number | null): void;
|
|
23
|
+
protected colWidth(i: number, v: number | null): void;
|
|
24
|
+
protected colDecimals(i: number, v: number | null): void;
|
|
25
|
+
protected moveColumn(i: number, d: number): void;
|
|
26
|
+
protected removeColumn(i: number): void;
|
|
27
|
+
protected addColumn(): void;
|
|
28
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TablePropsComponent, never>;
|
|
29
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<TablePropsComponent, "rpt-table-props", never, { "el": { "alias": "el"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PageBackground, PageSettings, Watermark } from '../../model/types';
|
|
2
|
+
import type { ViewPage } from './render';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/** One printed page: background, watermark, bands and their elements. */
|
|
5
|
+
export declare class PageViewComponent {
|
|
6
|
+
readonly page: import("@angular/core").InputSignal<ViewPage>;
|
|
7
|
+
readonly settings: import("@angular/core").InputSignal<PageSettings>;
|
|
8
|
+
readonly watermark: import("@angular/core").InputSignal<Watermark>;
|
|
9
|
+
readonly background: import("@angular/core").InputSignal<PageBackground>;
|
|
10
|
+
readonly watermarkText: import("@angular/core").InputSignal<string>;
|
|
11
|
+
readonly highlights: import("@angular/core").InputSignal<ReadonlySet<string>>;
|
|
12
|
+
readonly active: import("@angular/core").InputSignal<string | null>;
|
|
13
|
+
protected readonly contentWidth: import("@angular/core").Signal<number>;
|
|
14
|
+
protected readonly fit: import("@angular/core").Signal<"none" | "contain" | "cover" | "fill">;
|
|
15
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PageViewComponent, never>;
|
|
16
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PageViewComponent, "rpt-page-view", never, { "page": { "alias": "page"; "required": true; "isSignal": true; }; "settings": { "alias": "settings"; "required": true; "isSignal": true; }; "watermark": { "alias": "watermark"; "required": true; "isSignal": true; }; "background": { "alias": "background"; "required": true; "isSignal": true; }; "watermarkText": { "alias": "watermarkText"; "required": false; "isSignal": true; }; "highlights": { "alias": "highlights"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReportDefinition, ReportElement } from '../../model/types';
|
|
2
|
+
import { type Row } from '../../engine/expressions';
|
|
3
|
+
import type { TableRenderData } from '../../engine/table';
|
|
4
|
+
import type { ParamValues } from '../../engine/parameters';
|
|
5
|
+
export interface ViewItem {
|
|
6
|
+
/** Unique within the document: page:band:element. Used for find highlights. */
|
|
7
|
+
key: string;
|
|
8
|
+
el: ReportElement;
|
|
9
|
+
rows?: Row[];
|
|
10
|
+
top: number;
|
|
11
|
+
height: number;
|
|
12
|
+
content: string;
|
|
13
|
+
tableData?: TableRenderData;
|
|
14
|
+
/** Plain text of the item (content or table cells), for Find. */
|
|
15
|
+
text: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ViewBand {
|
|
18
|
+
top: number;
|
|
19
|
+
height: number;
|
|
20
|
+
items: ViewItem[];
|
|
21
|
+
}
|
|
22
|
+
export interface ViewPage {
|
|
23
|
+
index: number;
|
|
24
|
+
height: number;
|
|
25
|
+
background: boolean;
|
|
26
|
+
watermark: boolean;
|
|
27
|
+
bands: ViewBand[];
|
|
28
|
+
}
|
|
29
|
+
/** Group headers as a navigable tree. */
|
|
30
|
+
export interface Bookmark {
|
|
31
|
+
label: string;
|
|
32
|
+
page: number;
|
|
33
|
+
top: number;
|
|
34
|
+
children: Bookmark[];
|
|
35
|
+
}
|
|
36
|
+
export interface ViewDocument {
|
|
37
|
+
pages: ViewPage[];
|
|
38
|
+
bookmarks: Bookmark[];
|
|
39
|
+
rowCount: number;
|
|
40
|
+
watermarkText: string;
|
|
41
|
+
}
|
|
42
|
+
/** Lays out and evaluates the whole report into ready-to-draw pages, plus group bookmarks. */
|
|
43
|
+
export declare function buildDocument(report: ReportDefinition, rows: Row[], params: ParamValues, root: unknown): ViewDocument;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ReportParameter } from '../../model/types';
|
|
2
|
+
import { DesignerStore } from '../designer.store';
|
|
3
|
+
import { type Bookmark } from './render';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
type ViewMode = 'continuous' | 'single' | 'two';
|
|
6
|
+
type ZoomMode = 'width' | 'page' | number;
|
|
7
|
+
/**
|
|
8
|
+
* Report viewer: paged view with navigation, zoom, view modes, find, bookmarks, thumbnails,
|
|
9
|
+
* parameters, print and export. Used standalone (/viewer) and as the designer's preview.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ViewerComponent {
|
|
12
|
+
protected readonly store: DesignerStore;
|
|
13
|
+
/** Inside the designer (shows Close) vs. the standalone /viewer page. */
|
|
14
|
+
readonly embedded: import("@angular/core").InputSignal<boolean>;
|
|
15
|
+
/** Standalone viewer: shows a "Designer" link to this URL. */
|
|
16
|
+
readonly designerUrl: import("@angular/core").InputSignal<string | null | undefined>;
|
|
17
|
+
/** Standalone viewer: shows the Open button for local .report.json files. */
|
|
18
|
+
readonly allowOpen: import("@angular/core").InputSignal<boolean>;
|
|
19
|
+
private readonly host;
|
|
20
|
+
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
21
|
+
private readonly root;
|
|
22
|
+
private readonly scroll;
|
|
23
|
+
private readonly findBox;
|
|
24
|
+
protected readonly zoomSteps: number[];
|
|
25
|
+
protected readonly mode: import("@angular/core").WritableSignal<ViewMode>;
|
|
26
|
+
protected readonly zoomMode: import("@angular/core").WritableSignal<ZoomMode>;
|
|
27
|
+
protected readonly current: import("@angular/core").WritableSignal<number>;
|
|
28
|
+
protected readonly sidebar: import("@angular/core").WritableSignal<boolean>;
|
|
29
|
+
protected readonly sideTab: import("@angular/core").WritableSignal<"bookmarks" | "pages">;
|
|
30
|
+
protected readonly findOpen: import("@angular/core").WritableSignal<boolean>;
|
|
31
|
+
protected readonly query: import("@angular/core").WritableSignal<string>;
|
|
32
|
+
protected readonly matchIndex: import("@angular/core").WritableSignal<number>;
|
|
33
|
+
protected readonly paramsOpen: import("@angular/core").WritableSignal<boolean>;
|
|
34
|
+
private readonly viewport;
|
|
35
|
+
protected readonly settings: import("@angular/core").Signal<import("angular-my-report").PageSettings>;
|
|
36
|
+
protected readonly watermark: import("@angular/core").Signal<{
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
kind: "text" | "image";
|
|
39
|
+
text: string;
|
|
40
|
+
image: string;
|
|
41
|
+
opacity: number;
|
|
42
|
+
angle: number;
|
|
43
|
+
fontSize: number;
|
|
44
|
+
color: string;
|
|
45
|
+
bold: boolean;
|
|
46
|
+
imageScale: number;
|
|
47
|
+
layer: "behind" | "front";
|
|
48
|
+
pages: "all" | "first";
|
|
49
|
+
}>;
|
|
50
|
+
protected readonly background: import("@angular/core").Signal<{
|
|
51
|
+
enabled: boolean;
|
|
52
|
+
image: string;
|
|
53
|
+
fit: "cover" | "contain" | "stretch" | "center";
|
|
54
|
+
pages: "all" | "first";
|
|
55
|
+
}>;
|
|
56
|
+
protected readonly parameters: import("@angular/core").Signal<ReportParameter[]>;
|
|
57
|
+
protected readonly doc: import("@angular/core").Signal<import("./render").ViewDocument>;
|
|
58
|
+
protected readonly pageCount: import("@angular/core").Signal<number>;
|
|
59
|
+
protected readonly shownPages: import("@angular/core").Signal<import("./render").ViewPage[]>;
|
|
60
|
+
/** Effective zoom factor (fit modes depend on the viewport and the page size). */
|
|
61
|
+
protected readonly zoom: import("@angular/core").Signal<number>;
|
|
62
|
+
protected readonly zoomKey: import("@angular/core").Signal<string>;
|
|
63
|
+
protected readonly thumbScale: import("@angular/core").Signal<number>;
|
|
64
|
+
protected readonly matches: import("@angular/core").Signal<{
|
|
65
|
+
key: string;
|
|
66
|
+
page: number;
|
|
67
|
+
}[]>;
|
|
68
|
+
protected readonly highlightSet: import("@angular/core").Signal<ReadonlySet<string>>;
|
|
69
|
+
protected readonly activeKey: import("@angular/core").Signal<string>;
|
|
70
|
+
protected readonly draft: import("@angular/core").WritableSignal<Record<string, string>>;
|
|
71
|
+
constructor();
|
|
72
|
+
protected choices(p: ReportParameter): string[];
|
|
73
|
+
protected setDraft(name: string, value: unknown): void;
|
|
74
|
+
protected applyParams(): void;
|
|
75
|
+
protected asBookmarks(v: unknown): Bookmark[];
|
|
76
|
+
protected goTo(index: number, top?: number): void;
|
|
77
|
+
protected goToBookmark(b: Bookmark): void;
|
|
78
|
+
/** Continuous modes: the current page is the one at the top third of the viewport. */
|
|
79
|
+
protected onScroll(): void;
|
|
80
|
+
protected setMode(m: ViewMode): void;
|
|
81
|
+
protected setZoom(v: string): void;
|
|
82
|
+
protected zoomStep(dir: 1 | -1): void;
|
|
83
|
+
protected toggleFind(): void;
|
|
84
|
+
protected setQuery(q: string): void;
|
|
85
|
+
protected findStep(dir: 1 | -1): void;
|
|
86
|
+
private revealMatch;
|
|
87
|
+
/** Runs `fn` with every page rendered at 100% (single-page mode would otherwise print one page). */
|
|
88
|
+
private withAllPages;
|
|
89
|
+
protected print(): void;
|
|
90
|
+
protected save(kind: string): void;
|
|
91
|
+
/** The report's data rows (after the report filter) as CSV. */
|
|
92
|
+
private csv;
|
|
93
|
+
protected fullscreen(): void;
|
|
94
|
+
protected openFile(e: Event): Promise<void>;
|
|
95
|
+
protected onKey(e: KeyboardEvent): void;
|
|
96
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ViewerComponent, never>;
|
|
97
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ViewerComponent, "rpt-viewer", never, { "embedded": { "alias": "embedded"; "required": false; "isSignal": true; }; "designerUrl": { "alias": "designerUrl"; "required": false; "isSignal": true; }; "allowOpen": { "alias": "allowOpen"; "required": false; "isSignal": true; }; }, { "closed": "closed"; }, never, never, true, never>;
|
|
98
|
+
}
|
|
99
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DataSourceType, ReportDefinition } from '../model/types';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* The report viewer.
|
|
5
|
+
*
|
|
6
|
+
* ```html
|
|
7
|
+
* <myreport-viewer path="/reports/sales.report.json" datasource="/api/orders" type="json" />
|
|
8
|
+
* <myreport-viewer path="/reports/orders.report.json" datasource="https://host/odata/Orders" type="odata" />
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare class MyReportViewerComponent {
|
|
12
|
+
private readonly store;
|
|
13
|
+
/** URL of the .report.json to show. */
|
|
14
|
+
readonly path: import("@angular/core").InputSignal<string | null | undefined>;
|
|
15
|
+
/** A report definition (object or JSON string); used when `path` is empty. */
|
|
16
|
+
readonly report: import("@angular/core").InputSignal<string | ReportDefinition | null | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* The report's data, replacing its own data source: a URL (JSON file/API, or OData service / entity-set URL),
|
|
19
|
+
* a JSON string, or (as a property) an array/object. Empty = use the report's data source.
|
|
20
|
+
*/
|
|
21
|
+
readonly datasource: import("@angular/core").InputSignal<unknown>;
|
|
22
|
+
/** How `datasource` is read: `json` (default) or `odata`. */
|
|
23
|
+
readonly type: import("@angular/core").InputSignal<DataSourceType | null | undefined>;
|
|
24
|
+
/** Parameter values: object, JSON string, or query string (`Country=Egypt&Year=2024`). */
|
|
25
|
+
readonly params: import("@angular/core").InputSignal<string | Record<string, unknown> | null | undefined>;
|
|
26
|
+
/** Shows a "Designer" link in the toolbar pointing at this URL. */
|
|
27
|
+
readonly designerUrl: import("@angular/core").InputSignal<string | null | undefined>;
|
|
28
|
+
/** Shows the toolbar's Open button for local .report.json files (default true). */
|
|
29
|
+
readonly allowOpen: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
30
|
+
/** Fires with the report once it is loaded. */
|
|
31
|
+
readonly loaded: import("@angular/core").OutputEmitterRef<ReportDefinition>;
|
|
32
|
+
/** Fires with a message when the report or its data could not be loaded. */
|
|
33
|
+
readonly loadError: import("@angular/core").OutputEmitterRef<string>;
|
|
34
|
+
/** Fires with the rows once the data is loaded. */
|
|
35
|
+
readonly dataLoaded: import("@angular/core").OutputEmitterRef<import("../engine/expressions").Row[]>;
|
|
36
|
+
protected readonly error: import("@angular/core").WritableSignal<string | null>;
|
|
37
|
+
constructor();
|
|
38
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MyReportViewerComponent, never>;
|
|
39
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MyReportViewerComponent, "myreport-viewer", never, { "path": { "alias": "path"; "required": false; "isSignal": true; }; "report": { "alias": "report"; "required": false; "isSignal": true; }; "datasource": { "alias": "datasource"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "params": { "alias": "params"; "required": false; "isSignal": true; }; "designerUrl": { "alias": "designerUrl"; "required": false; "isSignal": true; }; "allowOpen": { "alias": "allowOpen"; "required": false; "isSignal": true; }; }, { "loaded": "loaded"; "loadError": "loadError"; "dataLoaded": "dataLoaded"; }, never, never, true, never>;
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "angular-my-report",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MyReport banded report designer and viewer for Angular: <myreport-designer> and <myreport-viewer>.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -14,13 +14,24 @@
|
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@angular/common": ">=
|
|
18
|
-
"@angular/core": ">=
|
|
19
|
-
"@angular/forms": ">=
|
|
17
|
+
"@angular/common": ">=18.1.0 <23.0.0",
|
|
18
|
+
"@angular/core": ">=18.1.0 <23.0.0",
|
|
19
|
+
"@angular/forms": ">=18.1.0 <23.0.0"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"qrcode-generator": "^1.4.4",
|
|
23
23
|
"tslib": "^2.3.0"
|
|
24
24
|
},
|
|
25
|
-
"sideEffects": false
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"module": "fesm2022/angular-my-report.mjs",
|
|
27
|
+
"typings": "index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
"./package.json": {
|
|
30
|
+
"default": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./index.d.ts",
|
|
34
|
+
"default": "./fesm2022/angular-my-report.mjs"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
26
37
|
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Public API of @mohamed351/angular
|
|
3
|
-
*/
|
|
4
1
|
export { MyReportDesignerComponent } from './lib/ui/designer.component';
|
|
5
2
|
export { MyReportViewerComponent } from './lib/ui/viewer.component';
|
|
6
3
|
export { overrideDataSource, type DataOverride } from './lib/ui/designer.store';
|
|
7
|
-
|
|
8
|
-
// Report model and engine helpers, for building or processing report definitions in code.
|
|
9
4
|
export * from './lib/model/types';
|
|
10
5
|
export { newReport, sampleReport, emptyDataSource } from './lib/model/defaults';
|
|
11
6
|
export { receiptReport } from './lib/model/factories';
|
package/ng-package.json
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import type { BarcodeSymbology } from '../model/types';
|
|
2
|
-
|
|
3
|
-
export interface BarcodeResult {
|
|
4
|
-
/** One char per module: '1' = bar, '0' = space. Empty when `error` is set. */
|
|
5
|
-
modules: string;
|
|
6
|
-
/** Human-readable text printed under the bars. */
|
|
7
|
-
text: string;
|
|
8
|
-
error?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Code 128 symbol widths (bar, space, bar, space, bar, space) for values 0–105, then STOP (106).
|
|
12
|
-
const CODE128 = [
|
|
13
|
-
'212222', '222122', '222221', '121223', '121322', '131222', '122213', '122312', '132212', '221213',
|
|
14
|
-
'221312', '231212', '112232', '122132', '122231', '113222', '123122', '123221', '223211', '221132',
|
|
15
|
-
'221231', '213212', '223112', '312131', '311222', '321122', '321221', '312212', '322112', '322211',
|
|
16
|
-
'212123', '212321', '232121', '111323', '131123', '131321', '112313', '132113', '132311', '211313',
|
|
17
|
-
'231113', '231311', '112133', '112331', '132131', '113123', '113321', '133121', '313121', '211331',
|
|
18
|
-
'231131', '213113', '213311', '213131', '311123', '311321', '331121', '312113', '312311', '332111',
|
|
19
|
-
'314111', '221411', '431111', '111224', '111422', '121124', '121421', '141122', '141221', '112214',
|
|
20
|
-
'112412', '122114', '122411', '142112', '142211', '241211', '221114', '413111', '241112', '134111',
|
|
21
|
-
'111242', '121142', '121241', '114212', '124112', '124211', '411212', '421112', '421211', '212141',
|
|
22
|
-
'214121', '412121', '111143', '111341', '131141', '114113', '114311', '411113', '411311', '113141',
|
|
23
|
-
'114131', '311141', '411131', '211412', '211214', '211232', '2331112',
|
|
24
|
-
];
|
|
25
|
-
const START_B = 104;
|
|
26
|
-
const START_C = 105;
|
|
27
|
-
const CODE_B = 100;
|
|
28
|
-
const CODE_C = 99;
|
|
29
|
-
const STOP = 106;
|
|
30
|
-
|
|
31
|
-
const widthsToModules = (w: string) => [...w].map((n, i) => (i % 2 === 0 ? '1' : '0').repeat(Number(n))).join('');
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Code 128: code set B for text, switching to code set C for runs of 4+ digits (denser).
|
|
35
|
-
* Supports printable ASCII (32–126).
|
|
36
|
-
*/
|
|
37
|
-
export function encodeCode128(value: string): BarcodeResult {
|
|
38
|
-
if (!value) return { modules: '', text: '', error: 'Empty value' };
|
|
39
|
-
if (![...value].every((c) => c.charCodeAt(0) >= 32 && c.charCodeAt(0) <= 126)) {
|
|
40
|
-
return { modules: '', text: value, error: 'Code 128 supports printable ASCII only' };
|
|
41
|
-
}
|
|
42
|
-
const digitRun = (i: number) => {
|
|
43
|
-
let n = 0;
|
|
44
|
-
while (i + n < value.length && value[i + n] >= '0' && value[i + n] <= '9') n++;
|
|
45
|
-
return n;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const codes: number[] = [];
|
|
49
|
-
let set: 'B' | 'C';
|
|
50
|
-
let i = 0;
|
|
51
|
-
const first = digitRun(0);
|
|
52
|
-
if (first >= 4) {
|
|
53
|
-
set = 'C';
|
|
54
|
-
codes.push(START_C);
|
|
55
|
-
} else {
|
|
56
|
-
set = 'B';
|
|
57
|
-
codes.push(START_B);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
while (i < value.length) {
|
|
61
|
-
const run = digitRun(i);
|
|
62
|
-
if (set === 'C') {
|
|
63
|
-
if (run >= 2) {
|
|
64
|
-
codes.push(Number(value.slice(i, i + 2)));
|
|
65
|
-
i += 2;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
codes.push(CODE_B);
|
|
69
|
-
set = 'B';
|
|
70
|
-
}
|
|
71
|
-
// Switch to C for a run of >= 4 digits; an odd run keeps its first digit in B.
|
|
72
|
-
if (run >= 4) {
|
|
73
|
-
if (run % 2 === 1) {
|
|
74
|
-
codes.push(value.charCodeAt(i) - 32);
|
|
75
|
-
i++;
|
|
76
|
-
}
|
|
77
|
-
codes.push(CODE_C);
|
|
78
|
-
set = 'C';
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
codes.push(value.charCodeAt(i) - 32);
|
|
82
|
-
i++;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const checksum = codes.reduce((sum, c, idx) => sum + c * (idx === 0 ? 1 : idx), 0) % 103;
|
|
86
|
-
codes.push(checksum, STOP);
|
|
87
|
-
return { modules: codes.map((c) => widthsToModules(CODE128[c])).join(''), text: value };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const EAN_L = ['0001101', '0011001', '0010011', '0111101', '0100011', '0110001', '0101111', '0111011', '0110111', '0001011'];
|
|
91
|
-
const EAN_R = EAN_L.map((p) => [...p].map((b) => (b === '1' ? '0' : '1')).join(''));
|
|
92
|
-
const EAN_G = EAN_R.map((p) => [...p].reverse().join(''));
|
|
93
|
-
const EAN_PARITY = ['LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG', 'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'];
|
|
94
|
-
|
|
95
|
-
export function ean13CheckDigit(first12: string): number {
|
|
96
|
-
const sum = [...first12].reduce((s, d, i) => s + Number(d) * (i % 2 === 0 ? 1 : 3), 0);
|
|
97
|
-
return (10 - (sum % 10)) % 10;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** EAN-13 from 12 digits (check digit added) or 13 digits (check digit verified). */
|
|
101
|
-
export function encodeEan13(value: string): BarcodeResult {
|
|
102
|
-
const v = value.replace(/\s+/g, '');
|
|
103
|
-
if (!/^\d{12,13}$/.test(v)) return { modules: '', text: value, error: 'EAN-13 needs 12 or 13 digits' };
|
|
104
|
-
const check = ean13CheckDigit(v.slice(0, 12));
|
|
105
|
-
if (v.length === 13 && Number(v[12]) !== check) return { modules: '', text: value, error: `Invalid check digit (expected ${check})` };
|
|
106
|
-
const digits = v.slice(0, 12) + check;
|
|
107
|
-
const parity = EAN_PARITY[Number(digits[0])];
|
|
108
|
-
let m = '101';
|
|
109
|
-
for (let k = 1; k <= 6; k++) m += (parity[k - 1] === 'L' ? EAN_L : EAN_G)[Number(digits[k])];
|
|
110
|
-
m += '01010';
|
|
111
|
-
for (let k = 7; k <= 12; k++) m += EAN_R[Number(digits[k])];
|
|
112
|
-
m += '101';
|
|
113
|
-
return { modules: m, text: digits };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function encodeBarcode(symbology: BarcodeSymbology, value: string): BarcodeResult {
|
|
117
|
-
return symbology === 'ean13' ? encodeEan13(value) : encodeCode128(value);
|
|
118
|
-
}
|
package/src/lib/engine/chart.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { ChartSpec } from '../model/types';
|
|
2
|
-
import { aggregate, getPath, type Row } from './expressions';
|
|
3
|
-
|
|
4
|
-
/** Validated categorical palette (light surface), assigned in fixed order — never cycled. */
|
|
5
|
-
export const SERIES_COLORS = ['#2a78d6', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#008300', '#6250d6', '#e34948'];
|
|
6
|
-
export const OTHER_COLOR = '#9a9a96';
|
|
7
|
-
export const MAX_SERIES = SERIES_COLORS.length;
|
|
8
|
-
|
|
9
|
-
export interface ChartData {
|
|
10
|
-
categories: string[];
|
|
11
|
-
series: { name: string; values: number[]; color: string }[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const toNum = (v: unknown) => (typeof v === 'number' && Number.isFinite(v) ? v : Number(v) || 0);
|
|
15
|
-
|
|
16
|
-
/** Groups rows by category and aggregates each value field into a series. */
|
|
17
|
-
export function chartData(spec: ChartSpec, rows: Row[]): ChartData {
|
|
18
|
-
const pie = spec.type === 'pie' || spec.type === 'doughnut';
|
|
19
|
-
const fields = (spec.valueFields.filter(Boolean).length ? spec.valueFields.filter(Boolean) : ['']).slice(0, pie ? 1 : MAX_SERIES);
|
|
20
|
-
|
|
21
|
-
const groups = new Map<string, Row[]>();
|
|
22
|
-
for (const r of rows) {
|
|
23
|
-
const key = spec.categoryField ? String(getPath(r, spec.categoryField) ?? '(blank)') : 'All';
|
|
24
|
-
let g = groups.get(key);
|
|
25
|
-
if (!g) groups.set(key, (g = []));
|
|
26
|
-
g.push(r);
|
|
27
|
-
}
|
|
28
|
-
let cats = [...groups.keys()].map((name) => ({
|
|
29
|
-
name,
|
|
30
|
-
rows: groups.get(name)!,
|
|
31
|
-
values: fields.map((f) => toNum(aggregate(spec.aggregate, f, groups.get(name)!))),
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
if (spec.sort === 'category') cats.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
35
|
-
else if (spec.sort === 'valueDesc') cats.sort((a, b) => b.values[0] - a.values[0]);
|
|
36
|
-
else if (spec.sort === 'valueAsc') cats.sort((a, b) => a.values[0] - b.values[0]);
|
|
37
|
-
|
|
38
|
-
// Pie slices beyond the palette (or maxCategories) fold into "Other"; other charts are just truncated.
|
|
39
|
-
const max = Math.max(1, pie ? Math.min(spec.maxCategories || MAX_SERIES, MAX_SERIES) : spec.maxCategories || 50);
|
|
40
|
-
if (cats.length > max) {
|
|
41
|
-
if (pie) {
|
|
42
|
-
const keep = cats.slice(0, max - 1);
|
|
43
|
-
const rest = cats.slice(max - 1).flatMap((c) => c.rows);
|
|
44
|
-
keep.push({ name: 'Other', rows: rest, values: fields.map((f) => toNum(aggregate(spec.aggregate, f, rest))) });
|
|
45
|
-
cats = keep;
|
|
46
|
-
} else {
|
|
47
|
-
cats = cats.slice(0, max);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (pie) {
|
|
52
|
-
return {
|
|
53
|
-
categories: cats.map((c) => c.name),
|
|
54
|
-
series: [{ name: fields[0] || 'Count', values: cats.map((c) => c.values[0]), color: SERIES_COLORS[0] }],
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
categories: cats.map((c) => c.name),
|
|
59
|
-
series: fields.map((f, i) => ({ name: f || 'Count', values: cats.map((c) => c.values[i]), color: SERIES_COLORS[i] })),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Pie slice colors: one fixed-order hue per category, "Other" in neutral gray. */
|
|
64
|
-
export const sliceColor = (name: string, i: number) => (name === 'Other' ? OTHER_COLOR : SERIES_COLORS[i % MAX_SERIES]);
|
|
65
|
-
|
|
66
|
-
/** "Nice" axis: rounded max and tick step for values in [min, max]. */
|
|
67
|
-
export function niceScale(min: number, max: number, ticks = 5): { min: number; max: number; step: number } {
|
|
68
|
-
const lo = Math.min(0, min);
|
|
69
|
-
const hi = Math.max(0, max);
|
|
70
|
-
if (hi === lo) return { min: lo, max: lo + 1, step: 0.2 };
|
|
71
|
-
const raw = (hi - lo) / ticks;
|
|
72
|
-
const mag = Math.pow(10, Math.floor(Math.log10(raw)));
|
|
73
|
-
const norm = raw / mag;
|
|
74
|
-
const step = (norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 2.5 ? 2.5 : norm <= 5 ? 5 : 10) * mag;
|
|
75
|
-
return { min: Math.floor(lo / step) * step, max: Math.ceil(hi / step) * step, step };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Short tick label: 1.2K, 3.4M. */
|
|
79
|
-
export function compactNumber(n: number): string {
|
|
80
|
-
const a = Math.abs(n);
|
|
81
|
-
if (a >= 1e9) return +(n / 1e9).toFixed(1) + 'B';
|
|
82
|
-
if (a >= 1e6) return +(n / 1e6).toFixed(1) + 'M';
|
|
83
|
-
if (a >= 1e3) return +(n / 1e3).toFixed(1) + 'K';
|
|
84
|
-
return String(+n.toFixed(2));
|
|
85
|
-
}
|