angular-my-report 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +99 -2
  2. package/ng-package.json +7 -0
  3. package/package.json +23 -3
  4. package/src/lib/engine/barcode.ts +118 -0
  5. package/src/lib/engine/chart.ts +85 -0
  6. package/src/lib/engine/chartLayout.ts +265 -0
  7. package/src/lib/engine/crosstab.ts +96 -0
  8. package/src/lib/engine/dataSources.ts +204 -0
  9. package/src/lib/engine/expressions.ts +734 -0
  10. package/src/lib/engine/measure.ts +62 -0
  11. package/src/lib/engine/parameters.ts +96 -0
  12. package/src/lib/engine/qr.ts +26 -0
  13. package/src/lib/engine/renderer.ts +324 -0
  14. package/src/lib/engine/table.ts +58 -0
  15. package/src/lib/model/defaults.ts +249 -0
  16. package/src/lib/model/factories.ts +324 -0
  17. package/src/lib/model/types.ts +281 -0
  18. package/src/lib/ui/css.ts +12 -0
  19. package/src/lib/ui/designer/banded-table-dialog.component.ts +127 -0
  20. package/src/lib/ui/designer/canvas.component.ts +281 -0
  21. package/src/lib/ui/designer/data-source-dialog.component.ts +201 -0
  22. package/src/lib/ui/designer/element-box.component.ts +138 -0
  23. package/src/lib/ui/designer/graphics.components.ts +140 -0
  24. package/src/lib/ui/designer/left-panel.component.ts +189 -0
  25. package/src/lib/ui/designer/safe-image.pipe.ts +16 -0
  26. package/src/lib/ui/designer/table-view.component.ts +120 -0
  27. package/src/lib/ui/designer.component.ts +296 -0
  28. package/src/lib/ui/designer.store.ts +230 -0
  29. package/src/lib/ui/inputs.ts +34 -0
  30. package/src/lib/ui/myreport.css +267 -0
  31. package/src/lib/ui/properties/band-props.component.ts +86 -0
  32. package/src/lib/ui/properties/element-props.component.ts +326 -0
  33. package/src/lib/ui/properties/expr-input.component.ts +191 -0
  34. package/src/lib/ui/properties/graphics-props.component.ts +260 -0
  35. package/src/lib/ui/properties/image-picker.component.ts +62 -0
  36. package/src/lib/ui/properties/properties-panel.component.ts +37 -0
  37. package/src/lib/ui/properties/report-extras.component.ts +198 -0
  38. package/src/lib/ui/properties/report-props.component.ts +211 -0
  39. package/src/lib/ui/properties/table-props.component.ts +197 -0
  40. package/src/lib/ui/viewer/page-view.component.ts +73 -0
  41. package/src/lib/ui/viewer/render.ts +161 -0
  42. package/src/lib/ui/viewer/viewer.component.ts +555 -0
  43. package/src/lib/ui/viewer.component.ts +107 -0
  44. package/src/public-api.ts +12 -0
  45. package/tsconfig.lib.json +12 -0
  46. package/tsconfig.lib.prod.json +5 -0
@@ -0,0 +1,267 @@
1
+ /* MyReport — styles scoped under .myreport (generated from the app stylesheet, then maintained by hand). */
2
+
3
+ .myreport {
4
+ --bg: #eef1f5;
5
+ --panel: #ffffff;
6
+ --border: #d5dae1;
7
+ --text: #1d2430;
8
+ --muted: #6b7686;
9
+ --accent: #2f6fde;
10
+ --accent-soft: #e5eefc;
11
+ --danger: #c4372f;
12
+ --ok: #217a45;
13
+ font-family: 'Segoe UI', system-ui, Arial, sans-serif;
14
+ font-size: 13px;
15
+ color: var(--text);
16
+ }
17
+ .myreport, .myreport * { box-sizing: border-box; }
18
+
19
+
20
+ .myreport button, .myreport input, .myreport select, .myreport textarea { font: inherit; color: inherit; }
21
+ .myreport button {
22
+ border: 1px solid var(--border); background: #fff; border-radius: 4px; padding: 4px 10px; cursor: pointer;
23
+ }
24
+ .myreport button:hover:not(:disabled) { border-color: var(--accent); }
25
+ .myreport button:disabled { opacity: 0.45; cursor: default; }
26
+ .myreport button.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
27
+ .myreport button.danger { color: var(--danger); }
28
+ .myreport button.link { border: none; background: none; color: var(--accent); padding: 0 4px; }
29
+ .myreport button.small { padding: 1px 6px; }
30
+ .myreport button.on { background: var(--accent-soft); border-color: var(--accent); }
31
+ .myreport input, .myreport select, .myreport textarea {
32
+ border: 1px solid var(--border); border-radius: 4px; padding: 3px 6px; background: #fff; min-width: 0;
33
+ }
34
+ .myreport input[type='color'] { padding: 0 2px; width: 40px; height: 24px; }
35
+ .myreport input[type='checkbox'] { width: auto; }
36
+ .myreport textarea { width: 100%; resize: vertical; font-family: Consolas, monospace; font-size: 12px; }
37
+ .myreport code { background: #f1f3f6; padding: 0 3px; border-radius: 3px; font-size: 11px; }
38
+ .myreport .muted { color: var(--muted); }
39
+ .myreport .error { color: var(--danger); }
40
+ .myreport .ok { color: var(--ok); }
41
+ .myreport .spacer { flex: 1; }
42
+ .myreport .sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 4px; }
43
+ .myreport .check { display: flex; align-items: center; gap: 6px; cursor: pointer; }
44
+
45
+ /* Layout */
46
+ .myreport .app { display: flex; flex-direction: column; height: 100%; }
47
+ .myreport .toolbar {
48
+ display: flex; align-items: center; gap: 6px; padding: 6px 10px; background: var(--panel);
49
+ border-bottom: 1px solid var(--border); flex-wrap: wrap;
50
+ }
51
+ .myreport .brand { font-weight: 700; margin-right: 8px; color: var(--accent); }
52
+ .myreport .workspace { flex: 1; display: grid; grid-template-columns: 230px 1fr 290px; min-height: 0; }
53
+ .myreport .panel { background: var(--panel); overflow: auto; padding: 8px 12px; display: flex; flex-direction: column; }
54
+ .myreport .left-panel { border-right: 1px solid var(--border); }
55
+ .myreport .right-panel { border-left: 1px solid var(--border); }
56
+ .myreport .panel h3 { margin: 6px 0 8px; font-size: 14px; display: flex; justify-content: space-between; align-items: center; }
57
+ .myreport .panel h4 { margin: 10px 0 6px; font-size: 12px; text-transform: uppercase; color: var(--muted); letter-spacing: 0.04em; }
58
+ .myreport .panel section { border-bottom: 1px solid #eef0f3; padding-bottom: 8px; margin-bottom: 4px; }
59
+ .myreport .panel section.grow { flex: 1; border: none; }
60
+ .myreport .hint { color: var(--muted); font-size: 11.5px; margin: 6px 0; line-height: 1.4; }
61
+
62
+ /* Toolbox & fields */
63
+ .myreport .toolbox { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
64
+ .myreport .tool {
65
+ border: 1px solid var(--border); border-radius: 4px; padding: 6px; cursor: grab; display: flex; gap: 6px;
66
+ align-items: center; user-select: none; background: #fafbfc;
67
+ }
68
+ .myreport .tool:hover, .myreport .field-item:hover { border-color: var(--accent); background: var(--accent-soft); }
69
+ .myreport .tool-icon { width: 18px; text-align: center; font-weight: 700; }
70
+ .myreport .ds-summary { word-break: break-all; margin-bottom: 4px; }
71
+ .myreport .ds-status { font-size: 12px; margin-bottom: 6px; }
72
+ .myreport .ds-status.error { color: var(--danger); }
73
+ .myreport .ds-status.ready { color: var(--ok); }
74
+ .myreport .field-list { display: flex; flex-direction: column; gap: 2px; }
75
+ .myreport .field-item {
76
+ border: 1px solid transparent; border-radius: 4px; padding: 3px 6px; cursor: grab; user-select: none;
77
+ display: flex; gap: 6px; align-items: center;
78
+ }
79
+ .myreport .field-type { width: 22px; font-size: 10px; color: var(--muted); text-align: center; font-weight: 600; }
80
+ .myreport .field-item.special .field-type { color: var(--accent); }
81
+
82
+ /* Properties */
83
+ .myreport .prop-row { display: grid; grid-template-columns: 82px 1fr; align-items: center; gap: 6px; margin: 4px 0; }
84
+ .myreport .prop-row > span { color: var(--muted); font-size: 12px; }
85
+ .myreport .prop-value { display: flex; gap: 4px; align-items: center; min-width: 0; }
86
+ .myreport .prop-value > input:not([type='color']):not([type='checkbox']), .myreport .prop-value > select { width: 100%; }
87
+ .myreport .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }
88
+ .myreport .grid2 .prop-row { grid-template-columns: 1fr 1.4fr; }
89
+ .myreport .btn-group { display: flex; gap: 2px; flex-wrap: wrap; margin: 6px 0; }
90
+ .myreport .btn-group button { padding: 2px 7px; min-width: 26px; }
91
+ .myreport .group-row { display: flex; gap: 4px; align-items: center; margin-bottom: 4px; }
92
+ .myreport .group-row input { flex: 1; }
93
+
94
+ /* Canvas */
95
+ .myreport .canvas-scroll { overflow: auto; padding: 24px; min-width: 0; }
96
+ .myreport .canvas-sizer { margin: 0 auto; position: relative; }
97
+ .myreport .canvas { transform-origin: 0 0; position: absolute; left: 0; top: 0; }
98
+ .myreport .band { position: relative; }
99
+ .myreport .band-title {
100
+ font-size: 11px; color: #fff; background: #8592a6; padding: 0 8px; display: flex; align-items: center;
101
+ cursor: pointer; user-select: none; border-radius: 3px 3px 0 0;
102
+ }
103
+ .myreport .band-title.band-detail { background: #4f7fc9; }
104
+ .myreport .band-title.band-groupHeader, .myreport .band-title.band-groupFooter { background: #3c9a76; }
105
+ .myreport .band-title.selected { outline: 2px solid var(--accent); outline-offset: 1px; }
106
+ .myreport .band-group { opacity: 0.85; margin-left: 2px; }
107
+ .myreport .band-body {
108
+ position: relative; background-color: #fff;
109
+ background-image: radial-gradient(circle, #d6dbe3 1px, transparent 1px);
110
+ background-size: 8px 8px; box-shadow: 0 0 0 1px var(--border);
111
+ }
112
+ .myreport .band-resizer { height: 6px; cursor: ns-resize; background: transparent; }
113
+ .myreport .band-resizer:hover { background: var(--accent-soft); }
114
+ .myreport .design-el { cursor: move; outline: 1px dashed rgba(80, 100, 130, 0.35); user-select: none; }
115
+ .myreport .design-el.kind-field { background-color: rgba(47, 111, 222, 0.06); }
116
+ .myreport .design-el.kind-line { outline-color: transparent; }
117
+ .myreport .design-el.selected { outline: 1.5px solid var(--accent); z-index: 2; }
118
+ .myreport .image-placeholder {
119
+ width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
120
+ background: repeating-linear-gradient(45deg, #f2f4f7, #f2f4f7 6px, #e6e9ee 6px, #e6e9ee 12px); color: var(--muted);
121
+ }
122
+ .myreport .handle { position: absolute; width: 8px; height: 8px; background: #fff; border: 1.5px solid var(--accent); z-index: 3; }
123
+ .myreport .handle-e { right: -5px; top: calc(50% - 4px); cursor: ew-resize; }
124
+ .myreport .handle-s { bottom: -5px; left: calc(50% - 4px); cursor: ns-resize; }
125
+ .myreport .handle-se { right: -5px; bottom: -5px; cursor: nwse-resize; }
126
+
127
+ /* Modal */
128
+ .myreport .modal-backdrop {
129
+ position: fixed; inset: 0; background: rgba(20, 28, 40, 0.4); display: flex; align-items: center;
130
+ justify-content: center; z-index: 50; padding: 16px;
131
+ }
132
+ .myreport .modal { background: #fff; border-radius: 8px; width: min(640px, 100%); max-height: 90vh; display: flex; flex-direction: column; box-shadow: 0 10px 40px rgba(0,0,0,.25); }
133
+ .myreport .modal header, .myreport .modal footer { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; }
134
+ .myreport .modal header { border-bottom: 1px solid var(--border); }
135
+ .myreport .modal header h2 { margin: 0; font-size: 16px; }
136
+ .myreport .modal footer { border-top: 1px solid var(--border); justify-content: flex-end; gap: 8px; }
137
+ .myreport .modal-body { padding: 12px 16px; overflow: auto; }
138
+ .myreport .tabs { display: flex; gap: 4px; margin-bottom: 10px; }
139
+ .myreport .field { display: flex; flex-direction: column; gap: 3px; margin-bottom: 10px; color: var(--muted); font-size: 12px; }
140
+ .myreport .field input, .myreport .field textarea { color: var(--text); font-size: 13px; }
141
+ .myreport .inline { display: flex; gap: 6px; }
142
+ .myreport .inline input { flex: 1; }
143
+ .myreport .url-preview { font-family: Consolas, monospace; font-size: 11px; word-break: break-all; margin-bottom: 10px; }
144
+ .myreport .test-area { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
145
+ .myreport .test-area .ok, .myreport .test-area .error { font-size: 12px; word-break: break-word; }
146
+
147
+ /* Preview */
148
+ .myreport .preview-overlay { position: fixed; inset: 0; background: #5d6672; z-index: 40; display: flex; flex-direction: column; }
149
+ .myreport .preview-toolbar { display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--panel); }
150
+ .myreport .preview-pages { flex: 1; overflow: auto; padding: 24px; display: flex; flex-direction: column; align-items: center; gap: 20px; }
151
+ .myreport .rpt-page { position: relative; background: #fff; box-shadow: 0 2px 12px rgba(0,0,0,.3); flex-shrink: 0; overflow: hidden; }
152
+
153
+ @media print {
154
+ .myreport .app, .myreport .preview-toolbar, .myreport .viewer-toolbar, .myreport .viewer-sidebar, .myreport .viewer-status, .myreport .find-bar, .myreport .viewer-msg { display: none !important; }
155
+ .myreport .viewer, .myreport .viewer.embedded, .myreport .viewer-body, .myreport .viewer-scroll { position: static !important; display: block !important; height: auto !important; overflow: visible !important; background: none !important; }
156
+ .myreport .viewer-pages { zoom: 1 !important; padding: 0 !important; display: block !important; }
157
+ .myreport .find-hit, .myreport .find-active { outline: none !important; }
158
+
159
+ .myreport .preview-overlay { position: static; background: none; display: block; }
160
+ .myreport .preview-pages { zoom: 1 !important; padding: 0; display: block; overflow: visible; }
161
+ .myreport .rpt-page { box-shadow: none; break-after: page; margin: 0; }
162
+ .myreport .rpt-page:last-child { break-after: auto; }
163
+ }
164
+
165
+ /* Angular host elements */
166
+
167
+ .myreport rpt-left-panel, .myreport rpt-properties-panel { display: contents; }
168
+ .myreport rpt-canvas { display: block; min-width: 0; overflow: hidden; }
169
+ .myreport rpt-canvas .canvas-scroll { height: 100%; }
170
+
171
+ /* Table properties */
172
+ .myreport .col-card { border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; margin-bottom: 8px; background: #fafbfc; }
173
+ .myreport .col-head { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
174
+ .myreport .design-el.kind-table { outline-style: solid; }
175
+
176
+ .myreport .tabs-label { align-self: center; margin-right: 4px; }
177
+
178
+ /* Header/Detail table wizard */
179
+ .myreport .wizard-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 12px; max-height: 220px; overflow: auto; border: 1px solid var(--border); border-radius: 4px; padding: 6px 8px; color: var(--text); margin-bottom: 4px; }
180
+ .myreport .design-el.kind-chart, .myreport .design-el.kind-crosstab, .myreport .design-el.kind-barcode, .myreport .design-el.kind-qrcode { outline-style: solid; }
181
+
182
+ /* Formula input with autocomplete */
183
+ .myreport .prop-label { font-size: 12px; color: var(--muted); margin: 6px 0 3px; }
184
+ .myreport rpt-expr-input { display: block; width: 100%; }
185
+ .myreport .expr-input { position: relative; width: 100%; }
186
+ .myreport .expr-input input, .myreport .expr-input textarea { width: 100%; font-family: Consolas, 'Courier New', monospace; font-size: 12px; }
187
+ .myreport .expr-error { color: var(--danger); font-size: 11px; margin-top: 2px; }
188
+ .myreport .ac-list {
189
+ position: absolute; left: 0; right: 0; top: 100%; z-index: 40; margin: 2px 0 0; padding: 2px 0; list-style: none;
190
+ background: #fff; border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 6px 20px rgba(20, 28, 40, .18);
191
+ max-height: 240px; overflow: auto; font-size: 12px;
192
+ }
193
+ .myreport .ac-list li { display: flex; gap: 6px; align-items: baseline; padding: 3px 8px; cursor: pointer; white-space: nowrap; }
194
+ .myreport .ac-list li .muted { overflow: hidden; text-overflow: ellipsis; }
195
+ .myreport .ac-list li.active, .myreport .ac-list li:hover { background: var(--accent-soft); }
196
+ .myreport .ac-kind { width: 18px; flex: none; text-align: center; font-size: 10px; font-weight: 700; color: var(--muted); }
197
+ .myreport .ac-kind.ac-fn { color: #6250d6; }
198
+ .myreport .ac-kind.ac-param { color: #eb6834; }
199
+ .myreport .ac-kind.ac-special { color: #1baf7a; }
200
+
201
+ /* Image picker */
202
+ .myreport .image-picker { display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
203
+ .myreport .image-picker > input { width: 100%; }
204
+ .myreport .image-picker .thumb { max-width: 100%; max-height: 90px; object-fit: contain; align-self: flex-start; border: 1px solid var(--border); border-radius: 4px; background: repeating-conic-gradient(#f1f3f6 0 25%, #fff 0 50%) 0 0 / 12px 12px; }
205
+
206
+ /* Preview: parameters, background, watermark */
207
+ .myreport .param-bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; padding: 8px 12px; background: #f6f8fb; border-top: 1px solid var(--border); }
208
+ .myreport .param-bar label { display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--muted); }
209
+ .myreport .rpt-page { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
210
+ .myreport .rpt-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-position: center; pointer-events: none; }
211
+ .myreport .rpt-watermark { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; overflow: hidden; z-index: 5; }
212
+ .myreport .rpt-watermark span { white-space: nowrap; font-family: 'Segoe UI', Arial, sans-serif; line-height: 1; }
213
+ @media print { .myreport .param-bar { display: none !important; } }
214
+
215
+ /* Receipts */
216
+ .myreport .new-menu { font-weight: 600; }
217
+ .myreport .rpt-page.roll { box-shadow: 0 2px 10px rgba(0,0,0,.3); border-radius: 2px; }
218
+
219
+ /* ---------------- Viewer ---------------- */
220
+ .myreport rpt-viewer { display: block; height: 100%; }
221
+ .myreport .viewer { display: flex; flex-direction: column; height: 100%; background: #525659; }
222
+ .myreport .viewer.embedded { position: fixed; inset: 0; z-index: 40; }
223
+ .myreport .viewer:fullscreen { height: 100vh; }
224
+ .myreport .viewer-toolbar { display: flex; align-items: center; gap: 3px; flex-wrap: wrap; padding: 5px 8px; background: #f5f6f8; border-bottom: 1px solid var(--border); }
225
+ .myreport .viewer-toolbar .vt, .myreport .find-bar .vt { min-width: 30px; padding: 3px 8px; background: transparent; border-color: transparent; border-radius: 4px; text-decoration: none; color: var(--text); display: inline-flex; align-items: center; gap: 4px; }
226
+ .myreport .viewer-toolbar .vt:hover:not(:disabled), .myreport .find-bar .vt:hover:not(:disabled) { background: #e4e8ee; border-color: var(--border); }
227
+ .myreport .viewer-toolbar .vt.on { background: var(--accent-soft); border-color: var(--accent); }
228
+ .myreport .vt-select { padding: 3px 4px; }
229
+ .myreport .vt-page { width: 52px; text-align: center; }
230
+ .myreport .vsep { width: 1px; align-self: stretch; background: var(--border); margin: 0 4px; }
231
+ .myreport .viewer-title { font-size: 13px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 30vw; }
232
+ .myreport .btn-link { border: 1px solid var(--border); background: #fff; border-radius: 4px; padding: 4px 10px; color: var(--text); text-decoration: none; }
233
+ .myreport .btn-link:hover { border-color: var(--accent); }
234
+ .myreport .find-bar { display: flex; align-items: center; gap: 4px; padding: 5px 10px; background: #fffbe6; border-bottom: 1px solid #f0e2a8; }
235
+ .myreport .find-bar input { width: 260px; }
236
+ .myreport .viewer-body { flex: 1; display: flex; min-height: 0; }
237
+ .myreport .viewer-sidebar { width: 230px; flex: none; background: #fff; border-right: 1px solid var(--border); overflow: auto; padding: 8px; }
238
+ .myreport .bm-tree, .myreport .bm-tree ul { list-style: none; margin: 0; padding-left: 12px; }
239
+ .myreport .bm-tree { padding-left: 0; }
240
+ .myreport .bm { border: none; background: none; padding: 3px 6px; width: 100%; text-align: left; border-radius: 4px; display: flex; justify-content: space-between; gap: 6px; }
241
+ .myreport .bm:hover { background: var(--accent-soft); }
242
+ .myreport .thumbs { display: flex; flex-direction: column; align-items: center; gap: 10px; }
243
+ .myreport .thumb { border: 2px solid transparent; background: none; padding: 4px; border-radius: 4px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
244
+ .myreport .thumb.on { border-color: var(--accent); }
245
+ .myreport .thumb-page { pointer-events: none; }
246
+ .myreport .thumb-page .rpt-page { box-shadow: 0 1px 4px rgba(0,0,0,.35); }
247
+ .myreport .viewer-scroll { flex: 1; overflow: auto; position: relative; }
248
+ .myreport .viewer-pages { display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 24px; width: max-content; min-width: 100%; }
249
+ .myreport .viewer-pages.mode-two { display: grid; grid-template-columns: repeat(2, max-content); justify-content: center; gap: 20px 24px; }
250
+ .myreport .viewer-pages .rpt-page { box-shadow: 0 2px 12px rgba(0,0,0,.45); }
251
+ .myreport .viewer-msg { position: sticky; top: 0; z-index: 2; padding: 6px 12px; background: #fff; text-align: center; }
252
+ .myreport .viewer-status { display: flex; gap: 18px; padding: 3px 12px; font-size: 12px; color: var(--muted); background: #f5f6f8; border-top: 1px solid var(--border); }
253
+ .myreport .find-hit { outline: 2px solid rgba(237, 161, 0, .8) !important; background-color: rgba(255, 214, 102, .35) !important; }
254
+ .myreport .find-active { outline: 3px solid #eb6834 !important; background-color: rgba(235, 104, 52, .25) !important; }
255
+
256
+ /* Component hosts */
257
+ myreport-designer, myreport-viewer { display: block; height: 100%; min-height: 0; }
258
+ .myreport .app { background: var(--bg); }
259
+ .myreport rpt-left-panel, .myreport rpt-properties-panel { display: contents; }
260
+
261
+ /* Printing: the viewer copies its pages into .myreport-print-root and hides the rest of the page. */
262
+ .myreport-print-root { display: none; }
263
+ @media print {
264
+ html.myreport-printing body > *:not(.myreport-print-root) { display: none !important; }
265
+ html.myreport-printing, html.myreport-printing body { background: #fff !important; height: auto !important; overflow: visible !important; }
266
+ html.myreport-printing .myreport-print-root { display: block !important; }
267
+ }
@@ -0,0 +1,86 @@
1
+ import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
2
+ import { FormsModule } from '@angular/forms';
3
+ import { BAND_LABELS } from '../../model/defaults';
4
+ import { DesignerStore } from '../designer.store';
5
+ import { removeGroup } from './report-props.component';
6
+
7
+ @Component({
8
+ selector: 'rpt-band-props',
9
+ standalone: true,
10
+ imports: [FormsModule],
11
+ changeDetection: ChangeDetectionStrategy.OnPush,
12
+ template: `
13
+ @if (band(); as b) {
14
+ <h3>{{ labels[b.type] }}</h3>
15
+ <section>
16
+ <label class="prop-row"><span>Height</span>
17
+ <div class="prop-value"><input type="number" min="0" [ngModel]="b.height" (ngModelChange)="setHeight($event)" /></div>
18
+ </label>
19
+ <div class="prop-row"><span>Elements</span><div class="prop-value">{{ b.elements.length }}</div></div>
20
+ </section>
21
+ @if (group(); as g) {
22
+ <section>
23
+ <h4>Group</h4>
24
+ <label class="prop-row"><span>Group by</span>
25
+ <div class="prop-value">
26
+ <input list="field-names" placeholder="Field name" [ngModel]="g.field" (ngModelChange)="setGroup('field', $event)" />
27
+ </div>
28
+ </label>
29
+ <label class="prop-row"><span>Order</span>
30
+ <div class="prop-value">
31
+ <select [ngModel]="g.direction" (ngModelChange)="setGroup('direction', $event)">
32
+ <option value="asc">Ascending</option>
33
+ <option value="desc">Descending</option>
34
+ </select>
35
+ </div>
36
+ </label>
37
+ <button class="danger" (click)="removeGroup(g.id)">Remove group</button>
38
+ </section>
39
+ }
40
+ @if (!b.groupId) {
41
+ <button class="danger" (click)="hide(b.id)">Remove section</button>
42
+ }
43
+ <p class="hint">Drag the bar under a band to resize it. Removed sections come back under Report → Sections. Aggregates in group bands cover the group's rows.</p>
44
+ }
45
+ `,
46
+ })
47
+ export class BandPropsComponent {
48
+ private readonly store = inject(DesignerStore);
49
+ readonly bandId = input.required<string>();
50
+ protected readonly labels = BAND_LABELS;
51
+
52
+ protected readonly band = computed(() => this.store.report().bands.find((b) => b.id === this.bandId()));
53
+ protected readonly group = computed(() => {
54
+ const gid = this.band()?.groupId;
55
+ return gid ? this.store.report().groups.find((g) => g.id === gid) : undefined;
56
+ });
57
+
58
+ protected setHeight(v: number | null): void {
59
+ if (v == null || !Number.isFinite(v)) return;
60
+ const id = this.bandId();
61
+ this.store.update((r) => (r.bands.find((b) => b.id === id)!.height = Math.max(0, v)));
62
+ }
63
+
64
+ /** Removes the section from the report (kept hidden with its elements, so it can be shown again). */
65
+ protected hide(id: string): void {
66
+ this.store.update((r) => {
67
+ const b = r.bands.find((x) => x.id === id);
68
+ if (b) b.visible = false;
69
+ });
70
+ this.store.select({ elementIds: [], bandId: null });
71
+ }
72
+
73
+ protected setGroup(k: 'field' | 'direction', v: string): void {
74
+ const gid = this.group()?.id;
75
+ this.store.update((r) => {
76
+ const g = r.groups.find((x) => x.id === gid);
77
+ if (!g) return;
78
+ if (k === 'field') g.field = v;
79
+ else g.direction = v as 'asc' | 'desc';
80
+ });
81
+ }
82
+
83
+ protected removeGroup(gid: string): void {
84
+ this.store.update((r) => removeGroup(r, gid));
85
+ }
86
+ }