@theseam/ui-common 1.0.2-beta.51 → 1.0.2-beta.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/theseam-ui-common-graphql.mjs +261 -67
- package/fesm2022/theseam-ui-common-graphql.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-states-counties-map.mjs +317 -0
- package/fesm2022/theseam-ui-common-states-counties-map.mjs.map +1 -0
- package/graphql/index.d.ts +186 -4
- package/package.json +5 -1
- package/states-counties-map/index.d.ts +120 -0
- package/states-counties-map/package.json +3 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable, input, booleanAttribute, output, viewChild, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { geoAlbers, geoPath } from 'd3-geo';
|
|
4
|
+
import { select } from 'd3-selection';
|
|
5
|
+
import { feature, mesh } from 'topojson-client';
|
|
6
|
+
import { ComponentHarness } from '@angular/cdk/testing';
|
|
7
|
+
|
|
8
|
+
/** Default URL used when no config is provided. */
|
|
9
|
+
const THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL = '/assets/geoData/us.json';
|
|
10
|
+
/** DI token for `TheSeamStatesCountiesMapConfig`. */
|
|
11
|
+
const THE_SEAM_STATES_COUNTIES_MAP_CONFIG = new InjectionToken('THE_SEAM_STATES_COUNTIES_MAP_CONFIG');
|
|
12
|
+
/**
|
|
13
|
+
* Register configuration for the states-counties map.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* bootstrapApplication(AppComponent, {
|
|
17
|
+
* providers: [
|
|
18
|
+
* provideStatesCountiesMap({ topologyUrl: '/static/us.json' }),
|
|
19
|
+
* ],
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function provideStatesCountiesMap(config) {
|
|
24
|
+
return { provide: THE_SEAM_STATES_COUNTIES_MAP_CONFIG, useValue: config };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Loads and caches TopoJSON topology data for the states/counties map.
|
|
29
|
+
*
|
|
30
|
+
* Provided in root so that multiple component instances share a single
|
|
31
|
+
* in-flight/cached request per URL.
|
|
32
|
+
*/
|
|
33
|
+
class TheSeamStatesCountiesMapDataService {
|
|
34
|
+
_config = inject(THE_SEAM_STATES_COUNTIES_MAP_CONFIG, {
|
|
35
|
+
optional: true,
|
|
36
|
+
});
|
|
37
|
+
_cache = new Map();
|
|
38
|
+
/**
|
|
39
|
+
* Load the topology from the configured URL (or an explicit override).
|
|
40
|
+
* Concurrent calls for the same URL share a single fetch promise.
|
|
41
|
+
*/
|
|
42
|
+
load(url) {
|
|
43
|
+
const resolvedUrl = url ??
|
|
44
|
+
this._config?.topologyUrl ??
|
|
45
|
+
THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL;
|
|
46
|
+
const existing = this._cache.get(resolvedUrl);
|
|
47
|
+
if (existing) {
|
|
48
|
+
return existing;
|
|
49
|
+
}
|
|
50
|
+
const promise = fetch(resolvedUrl).then(async (res) => {
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(`Failed to load topology from ${resolvedUrl}: ${res.status} ${res.statusText}`);
|
|
53
|
+
}
|
|
54
|
+
return (await res.json());
|
|
55
|
+
});
|
|
56
|
+
// Evict on rejection so a transient failure doesn't poison the cache.
|
|
57
|
+
promise.catch(() => this._cache.delete(resolvedUrl));
|
|
58
|
+
this._cache.set(resolvedUrl, promise);
|
|
59
|
+
return promise;
|
|
60
|
+
}
|
|
61
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamStatesCountiesMapDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
62
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamStatesCountiesMapDataService, providedIn: 'root' });
|
|
63
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamStatesCountiesMapDataService, decorators: [{
|
|
65
|
+
type: Injectable,
|
|
66
|
+
args: [{ providedIn: 'root' }]
|
|
67
|
+
}] });
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extract the state FIPS prefix from a county FIPS code.
|
|
71
|
+
*
|
|
72
|
+
* County codes are either 4- or 5-digit numeric ids (as strings or numbers
|
|
73
|
+
* depending on the source). The state portion is everything except the last
|
|
74
|
+
* three digits — i.e. 1 or 2 characters.
|
|
75
|
+
*/
|
|
76
|
+
function stateIdFromCountyId(countyId) {
|
|
77
|
+
const asString = `${countyId}`;
|
|
78
|
+
return asString.slice(0, asString.length - 3);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether the given county id appears in the selection list.
|
|
82
|
+
*
|
|
83
|
+
* Comparison is numeric so that `1001` and `"01001"` are treated as equal,
|
|
84
|
+
* matching the historical behavior of the Cotton app component.
|
|
85
|
+
*/
|
|
86
|
+
function isCountySelected(countyId, selectedCountyIds) {
|
|
87
|
+
if (!selectedCountyIds || selectedCountyIds.length === 0) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const target = Number(countyId);
|
|
91
|
+
for (const id of selectedCountyIds) {
|
|
92
|
+
if (Number(id) === target) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class TheSeamStatesCountiesMapComponent {
|
|
100
|
+
/** FIPS state code (e.g., `"48"` for Texas). Null/undefined renders nothing. */
|
|
101
|
+
stateNumber = input(undefined, ...(ngDevMode ? [{ debugName: "stateNumber" }] : []));
|
|
102
|
+
/** FIPS county codes to highlight with the `county-selected` class. */
|
|
103
|
+
selectedCountyIds = input([], ...(ngDevMode ? [{ debugName: "selectedCountyIds" }] : []));
|
|
104
|
+
/** Enable pointer interaction (click, enter, leave) on counties. */
|
|
105
|
+
interactive = input(false, ...(ngDevMode ? [{ debugName: "interactive", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
106
|
+
countyClick = output();
|
|
107
|
+
countyEnter = output();
|
|
108
|
+
countyLeave = output();
|
|
109
|
+
_wrapper = viewChild.required('wrapper');
|
|
110
|
+
_data = inject(TheSeamStatesCountiesMapDataService);
|
|
111
|
+
_topologyPromise = null;
|
|
112
|
+
_lastRenderedState = null;
|
|
113
|
+
_lastRenderedInteractive = null;
|
|
114
|
+
_renderSerial = 0;
|
|
115
|
+
constructor() {
|
|
116
|
+
// Re-render whenever the state number or interactive mode changes.
|
|
117
|
+
effect(() => {
|
|
118
|
+
const state = this.stateNumber() ?? null;
|
|
119
|
+
const inter = this.interactive();
|
|
120
|
+
if (state !== this._lastRenderedState ||
|
|
121
|
+
inter !== this._lastRenderedInteractive) {
|
|
122
|
+
this._lastRenderedState = state;
|
|
123
|
+
this._lastRenderedInteractive = inter;
|
|
124
|
+
void this._render();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// Re-apply selection classes whenever selection changes (no full re-render).
|
|
128
|
+
effect(() => {
|
|
129
|
+
// Touch the signal so the effect tracks it.
|
|
130
|
+
this.selectedCountyIds();
|
|
131
|
+
this._updateSelectedCounties();
|
|
132
|
+
});
|
|
133
|
+
// Reflow on container resize.
|
|
134
|
+
effect((onCleanup) => {
|
|
135
|
+
const host = this._wrapper().nativeElement;
|
|
136
|
+
const observer = new ResizeObserver(() => void this._render());
|
|
137
|
+
observer.observe(host);
|
|
138
|
+
onCleanup(() => observer.disconnect());
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
_loadTopology() {
|
|
142
|
+
if (!this._topologyPromise) {
|
|
143
|
+
this._topologyPromise = this._data.load();
|
|
144
|
+
}
|
|
145
|
+
return this._topologyPromise;
|
|
146
|
+
}
|
|
147
|
+
async _render() {
|
|
148
|
+
const serial = ++this._renderSerial;
|
|
149
|
+
const host = this._wrapper().nativeElement;
|
|
150
|
+
const rect = host.getBoundingClientRect();
|
|
151
|
+
const width = rect.width;
|
|
152
|
+
const height = rect.height;
|
|
153
|
+
select(host).select('svg').remove();
|
|
154
|
+
const state = this.stateNumber();
|
|
155
|
+
if (!state) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const topology = await this._loadTopology();
|
|
159
|
+
if (serial !== this._renderSerial) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const statesLayer = topology.objects['states'];
|
|
163
|
+
const countiesLayer = topology.objects['counties'];
|
|
164
|
+
const states = feature(topology, statesLayer);
|
|
165
|
+
const stateFeature = states.features.find((d) => Number(d.id) === Number(state));
|
|
166
|
+
if (!stateFeature) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const projection = geoAlbers();
|
|
170
|
+
const path = geoPath().projection(projection);
|
|
171
|
+
projection.scale(1).translate([0, 0]);
|
|
172
|
+
const b = path.bounds(stateFeature);
|
|
173
|
+
const s = 0.95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height);
|
|
174
|
+
const t = [
|
|
175
|
+
(width - s * (b[1][0] + b[0][0])) / 2,
|
|
176
|
+
(height - s * (b[1][1] + b[0][1])) / 2,
|
|
177
|
+
];
|
|
178
|
+
projection.scale(s).translate(t);
|
|
179
|
+
const svg = select(host)
|
|
180
|
+
.append('svg')
|
|
181
|
+
.attr('width', width)
|
|
182
|
+
.attr('height', height);
|
|
183
|
+
svg
|
|
184
|
+
.append('path')
|
|
185
|
+
.datum(mesh(topology, statesLayer, (a, b1) => a !== b1))
|
|
186
|
+
.attr('class', 'mesh')
|
|
187
|
+
.attr('d', path);
|
|
188
|
+
svg
|
|
189
|
+
.append('path')
|
|
190
|
+
.datum(stateFeature)
|
|
191
|
+
.attr('class', 'outline')
|
|
192
|
+
.attr('d', path)
|
|
193
|
+
.attr('id', 'land');
|
|
194
|
+
svg
|
|
195
|
+
.append('clipPath')
|
|
196
|
+
.attr('id', 'clip-land')
|
|
197
|
+
.append('use')
|
|
198
|
+
.attr('xlink:href', '#land');
|
|
199
|
+
const counties = feature(topology, countiesLayer);
|
|
200
|
+
const stateNum = `${parseInt(state, 10)}`;
|
|
201
|
+
const stateCounties = counties.features.filter((d) => stateIdFromCountyId(d.id) === stateNum);
|
|
202
|
+
const isInteractive = this.interactive();
|
|
203
|
+
const countyPaths = svg
|
|
204
|
+
.selectAll('path[county-id]')
|
|
205
|
+
.data(stateCounties)
|
|
206
|
+
.enter()
|
|
207
|
+
.append('path')
|
|
208
|
+
.attr('d', path)
|
|
209
|
+
.attr('county-id', (d) => `${d.id}`.padStart(5, '0'));
|
|
210
|
+
if (isInteractive) {
|
|
211
|
+
countyPaths
|
|
212
|
+
.on('click', (_event, d) => {
|
|
213
|
+
this.countyClick.emit({
|
|
214
|
+
id: `${d.id}`.padStart(5, '0'),
|
|
215
|
+
feature: d,
|
|
216
|
+
});
|
|
217
|
+
})
|
|
218
|
+
.on('mouseenter', (_event, d) => {
|
|
219
|
+
select(_event.currentTarget).classed('county-hover', true);
|
|
220
|
+
this.countyEnter.emit({
|
|
221
|
+
id: `${d.id}`.padStart(5, '0'),
|
|
222
|
+
feature: d,
|
|
223
|
+
});
|
|
224
|
+
})
|
|
225
|
+
.on('mouseleave', (_event, d) => {
|
|
226
|
+
select(_event.currentTarget).classed('county-hover', false);
|
|
227
|
+
this.countyLeave.emit({
|
|
228
|
+
id: `${d.id}`.padStart(5, '0'),
|
|
229
|
+
feature: d,
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
this._updateSelectedCounties();
|
|
234
|
+
}
|
|
235
|
+
_updateSelectedCounties() {
|
|
236
|
+
const host = this._wrapper().nativeElement;
|
|
237
|
+
const selected = this.selectedCountyIds();
|
|
238
|
+
select(host)
|
|
239
|
+
.select('svg')
|
|
240
|
+
.selectAll('path[county-id]')
|
|
241
|
+
.attr('class', (d) => isCountySelected(d.id, selected)
|
|
242
|
+
? 'county-selected'
|
|
243
|
+
: 'county');
|
|
244
|
+
}
|
|
245
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamStatesCountiesMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
246
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.15", type: TheSeamStatesCountiesMapComponent, isStandalone: true, selector: "seam-states-counties-map", inputs: { stateNumber: { classPropertyName: "stateNumber", publicName: "stateNumber", isSignal: true, isRequired: false, transformFunction: null }, selectedCountyIds: { classPropertyName: "selectedCountyIds", publicName: "selectedCountyIds", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { countyClick: "countyClick", countyEnter: "countyEnter", countyLeave: "countyLeave" }, host: { properties: { "class.seam-interactive": "interactive()" } }, viewQueries: [{ propertyName: "_wrapper", first: true, predicate: ["wrapper"], descendants: true, isSignal: true }], ngImport: i0, template: `<div #wrapper class="states-counties-map-wrapper"></div>`, isInline: true, styles: ["seam-states-counties-map{display:block;height:100%}seam-states-counties-map .states-counties-map-wrapper{width:100%;height:100%}seam-states-counties-map .outline{fill:none;stroke:#000;stroke-width:1.5px}seam-states-counties-map .mesh{fill:none;stroke:#fff;stroke-width:.5px;stroke-linejoin:round}seam-states-counties-map path{stroke-width:.5px;fill:none;stroke:#000}seam-states-counties-map .county{fill:transparent}seam-states-counties-map .county-selected{fill:#00f}seam-states-counties-map.seam-interactive .county,seam-states-counties-map.seam-interactive .county-selected{cursor:pointer}seam-states-counties-map .county-hover:not(.county-selected){fill:#0000001a}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
247
|
+
}
|
|
248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamStatesCountiesMapComponent, decorators: [{
|
|
249
|
+
type: Component,
|
|
250
|
+
args: [{ selector: 'seam-states-counties-map', template: `<div #wrapper class="states-counties-map-wrapper"></div>`, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.seam-interactive]': 'interactive()' }, styles: ["seam-states-counties-map{display:block;height:100%}seam-states-counties-map .states-counties-map-wrapper{width:100%;height:100%}seam-states-counties-map .outline{fill:none;stroke:#000;stroke-width:1.5px}seam-states-counties-map .mesh{fill:none;stroke:#fff;stroke-width:.5px;stroke-linejoin:round}seam-states-counties-map path{stroke-width:.5px;fill:none;stroke:#000}seam-states-counties-map .county{fill:transparent}seam-states-counties-map .county-selected{fill:#00f}seam-states-counties-map.seam-interactive .county,seam-states-counties-map.seam-interactive .county-selected{cursor:pointer}seam-states-counties-map .county-hover:not(.county-selected){fill:#0000001a}\n"] }]
|
|
251
|
+
}], ctorParameters: () => [], propDecorators: { stateNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateNumber", required: false }] }], selectedCountyIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedCountyIds", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], countyClick: [{ type: i0.Output, args: ["countyClick"] }], countyEnter: [{ type: i0.Output, args: ["countyEnter"] }], countyLeave: [{ type: i0.Output, args: ["countyLeave"] }], _wrapper: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }] } });
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Harness for `TheSeamStatesCountiesMapComponent`. Designed for use in both
|
|
255
|
+
* TestBed and Storybook (via `@marklb/storybook-harness`).
|
|
256
|
+
*/
|
|
257
|
+
class TheSeamStatesCountiesMapHarness extends ComponentHarness {
|
|
258
|
+
static hostSelector = 'seam-states-counties-map';
|
|
259
|
+
_wrapper = this.locatorFor('.states-counties-map-wrapper');
|
|
260
|
+
_svg = this.locatorForOptional('svg');
|
|
261
|
+
/** Whether the SVG has been rendered yet. */
|
|
262
|
+
async hasRendered() {
|
|
263
|
+
return (await this._svg()) !== null;
|
|
264
|
+
}
|
|
265
|
+
/** Returns all county path elements that have been rendered. */
|
|
266
|
+
async getCountyPaths() {
|
|
267
|
+
return this.locatorForAll('path[county-id]')();
|
|
268
|
+
}
|
|
269
|
+
/** Returns the path for a single county by its FIPS id, or null. */
|
|
270
|
+
async getCountyPath(countyId) {
|
|
271
|
+
const matches = await this.locatorForAll(`path[county-id="${countyId}"]`)();
|
|
272
|
+
return matches[0] ?? null;
|
|
273
|
+
}
|
|
274
|
+
/** Ids of every county currently marked `county-selected`. */
|
|
275
|
+
async getSelectedCountyIds() {
|
|
276
|
+
const paths = await this.locatorForAll('path.county-selected')();
|
|
277
|
+
const ids = await Promise.all(paths.map((p) => p.getAttribute('county-id')));
|
|
278
|
+
return ids.filter((id) => id !== null);
|
|
279
|
+
}
|
|
280
|
+
/** Click a county by FIPS id. Throws if the county is not rendered. */
|
|
281
|
+
async clickCounty(countyId) {
|
|
282
|
+
const path = await this.getCountyPath(countyId);
|
|
283
|
+
if (!path) {
|
|
284
|
+
throw new Error(`TheSeamStatesCountiesMapHarness.clickCounty: county ${countyId} is not rendered`);
|
|
285
|
+
}
|
|
286
|
+
await path.click();
|
|
287
|
+
}
|
|
288
|
+
/** Simulate pointer entering a county area. Throws if the county is not rendered. */
|
|
289
|
+
async enterCounty(countyId) {
|
|
290
|
+
const path = await this.getCountyPath(countyId);
|
|
291
|
+
if (!path) {
|
|
292
|
+
throw new Error(`TheSeamStatesCountiesMapHarness.enterCounty: county ${countyId} is not rendered`);
|
|
293
|
+
}
|
|
294
|
+
await path.hover();
|
|
295
|
+
}
|
|
296
|
+
/** Simulate pointer leaving a county area by hovering away from it. */
|
|
297
|
+
async leaveCounty(countyId) {
|
|
298
|
+
const path = await this.getCountyPath(countyId);
|
|
299
|
+
if (!path) {
|
|
300
|
+
throw new Error(`TheSeamStatesCountiesMapHarness.leaveCounty: county ${countyId} is not rendered`);
|
|
301
|
+
}
|
|
302
|
+
// Move pointer to the wrapper (outside any county) to trigger mouseleave.
|
|
303
|
+
await (await this.locatorFor('.states-counties-map-wrapper')()).hover();
|
|
304
|
+
}
|
|
305
|
+
/** Rendered viewport dimensions, for layout-sensitive assertions. */
|
|
306
|
+
async getWrapperSize() {
|
|
307
|
+
const rect = await (await this._wrapper()).getDimensions();
|
|
308
|
+
return { width: rect.width, height: rect.height };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Generated bundle index. Do not edit.
|
|
314
|
+
*/
|
|
315
|
+
|
|
316
|
+
export { THE_SEAM_STATES_COUNTIES_MAP_CONFIG, THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL, TheSeamStatesCountiesMapComponent, TheSeamStatesCountiesMapDataService, TheSeamStatesCountiesMapHarness, provideStatesCountiesMap };
|
|
317
|
+
//# sourceMappingURL=theseam-ui-common-states-counties-map.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theseam-ui-common-states-counties-map.mjs","sources":["../../../projects/ui-common/states-counties-map/states-counties-map-config.ts","../../../projects/ui-common/states-counties-map/states-counties-map-data.service.ts","../../../projects/ui-common/states-counties-map/states-counties-map.helpers.ts","../../../projects/ui-common/states-counties-map/states-counties-map.component.ts","../../../projects/ui-common/states-counties-map/testing/states-counties-map.harness.ts","../../../projects/ui-common/states-counties-map/theseam-ui-common-states-counties-map.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core'\n\n/** Configuration for `TheSeamStatesCountiesMapComponent`. */\nexport interface TheSeamStatesCountiesMapConfig {\n /**\n * URL to the TopoJSON topology file.\n *\n * Defaults to `/assets/geoData/us.json` when the token is not provided.\n * The file is not shipped with the library — each consuming app must\n * place a compatible topology at this path or provide a different URL\n * via `provideStatesCountiesMap({ topologyUrl })`.\n */\n readonly topologyUrl?: string\n}\n\n/** Default URL used when no config is provided. */\nexport const THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL =\n '/assets/geoData/us.json'\n\n/** DI token for `TheSeamStatesCountiesMapConfig`. */\nexport const THE_SEAM_STATES_COUNTIES_MAP_CONFIG =\n new InjectionToken<TheSeamStatesCountiesMapConfig>(\n 'THE_SEAM_STATES_COUNTIES_MAP_CONFIG',\n )\n\n/**\n * Register configuration for the states-counties map.\n *\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideStatesCountiesMap({ topologyUrl: '/static/us.json' }),\n * ],\n * })\n * ```\n */\nexport function provideStatesCountiesMap(\n config: TheSeamStatesCountiesMapConfig,\n): Provider {\n return { provide: THE_SEAM_STATES_COUNTIES_MAP_CONFIG, useValue: config }\n}\n","import { inject, Injectable } from '@angular/core'\nimport type { Topology } from 'topojson-specification'\n\nimport {\n THE_SEAM_STATES_COUNTIES_MAP_CONFIG,\n THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL,\n} from './states-counties-map-config'\n\n/**\n * Loads and caches TopoJSON topology data for the states/counties map.\n *\n * Provided in root so that multiple component instances share a single\n * in-flight/cached request per URL.\n */\n@Injectable({ providedIn: 'root' })\nexport class TheSeamStatesCountiesMapDataService {\n private readonly _config = inject(THE_SEAM_STATES_COUNTIES_MAP_CONFIG, {\n optional: true,\n })\n\n private readonly _cache = new Map<string, Promise<Topology>>()\n\n /**\n * Load the topology from the configured URL (or an explicit override).\n * Concurrent calls for the same URL share a single fetch promise.\n */\n load(url?: string): Promise<Topology> {\n const resolvedUrl =\n url ??\n this._config?.topologyUrl ??\n THE_SEAM_STATES_COUNTIES_MAP_DEFAULT_URL\n\n const existing = this._cache.get(resolvedUrl)\n if (existing) {\n return existing\n }\n\n const promise = fetch(resolvedUrl).then(async (res) => {\n if (!res.ok) {\n throw new Error(\n `Failed to load topology from ${resolvedUrl}: ${res.status} ${res.statusText}`,\n )\n }\n return (await res.json()) as Topology\n })\n\n // Evict on rejection so a transient failure doesn't poison the cache.\n promise.catch(() => this._cache.delete(resolvedUrl))\n this._cache.set(resolvedUrl, promise)\n return promise\n }\n}\n","/**\n * Extract the state FIPS prefix from a county FIPS code.\n *\n * County codes are either 4- or 5-digit numeric ids (as strings or numbers\n * depending on the source). The state portion is everything except the last\n * three digits — i.e. 1 or 2 characters.\n */\nexport function stateIdFromCountyId(countyId: string | number): string {\n const asString = `${countyId}`\n return asString.slice(0, asString.length - 3)\n}\n\n/**\n * Whether the given county id appears in the selection list.\n *\n * Comparison is numeric so that `1001` and `\"01001\"` are treated as equal,\n * matching the historical behavior of the Cotton app component.\n */\nexport function isCountySelected(\n countyId: string | number,\n selectedCountyIds: readonly (string | number)[] | null | undefined,\n): boolean {\n if (!selectedCountyIds || selectedCountyIds.length === 0) {\n return false\n }\n const target = Number(countyId)\n for (const id of selectedCountyIds) {\n if (Number(id) === target) {\n return true\n }\n }\n return false\n}\n","import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n effect,\n inject,\n input,\n output,\n viewChild,\n ViewEncapsulation,\n} from '@angular/core'\nimport type { Feature, FeatureCollection, Geometry } from 'geojson'\nimport { geoAlbers, geoPath, type GeoPath } from 'd3-geo'\nimport { select, type Selection } from 'd3-selection'\nimport { feature as topoFeature, mesh as topoMesh } from 'topojson-client'\nimport type { GeometryCollection, Topology } from 'topojson-specification'\n\nimport { TheSeamStatesCountiesMapCountyEvent } from './states-counties-map.models'\nimport { TheSeamStatesCountiesMapDataService } from './states-counties-map-data.service'\nimport {\n isCountySelected,\n stateIdFromCountyId,\n} from './states-counties-map.helpers'\n\n@Component({\n selector: 'seam-states-counties-map',\n template: `<div #wrapper class=\"states-counties-map-wrapper\"></div>`,\n styleUrls: ['./states-counties-map.component.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class.seam-interactive]': 'interactive()' },\n})\nexport class TheSeamStatesCountiesMapComponent {\n /** FIPS state code (e.g., `\"48\"` for Texas). Null/undefined renders nothing. */\n readonly stateNumber = input<string | null | undefined>(undefined)\n\n /** FIPS county codes to highlight with the `county-selected` class. */\n readonly selectedCountyIds = input<readonly string[]>([])\n\n /** Enable pointer interaction (click, enter, leave) on counties. */\n readonly interactive = input(false, { transform: booleanAttribute })\n\n readonly countyClick = output<TheSeamStatesCountiesMapCountyEvent>()\n readonly countyEnter = output<TheSeamStatesCountiesMapCountyEvent>()\n readonly countyLeave = output<TheSeamStatesCountiesMapCountyEvent>()\n\n private readonly _wrapper =\n viewChild.required<ElementRef<HTMLDivElement>>('wrapper')\n\n private readonly _data = inject(TheSeamStatesCountiesMapDataService)\n\n private _topologyPromise: Promise<Topology> | null = null\n private _lastRenderedState: string | null = null\n private _lastRenderedInteractive: boolean | null = null\n private _renderSerial = 0\n\n constructor() {\n // Re-render whenever the state number or interactive mode changes.\n effect(() => {\n const state = this.stateNumber() ?? null\n const inter = this.interactive()\n if (\n state !== this._lastRenderedState ||\n inter !== this._lastRenderedInteractive\n ) {\n this._lastRenderedState = state\n this._lastRenderedInteractive = inter\n void this._render()\n }\n })\n\n // Re-apply selection classes whenever selection changes (no full re-render).\n effect(() => {\n // Touch the signal so the effect tracks it.\n this.selectedCountyIds()\n this._updateSelectedCounties()\n })\n\n // Reflow on container resize.\n effect((onCleanup) => {\n const host = this._wrapper().nativeElement\n const observer = new ResizeObserver(() => void this._render())\n observer.observe(host)\n onCleanup(() => observer.disconnect())\n })\n }\n\n private _loadTopology(): Promise<Topology> {\n if (!this._topologyPromise) {\n this._topologyPromise = this._data.load()\n }\n return this._topologyPromise\n }\n\n private async _render(): Promise<void> {\n const serial = ++this._renderSerial\n const host = this._wrapper().nativeElement\n const rect = host.getBoundingClientRect()\n const width = rect.width\n const height = rect.height\n\n select(host).select('svg').remove()\n\n const state = this.stateNumber()\n if (!state) {\n return\n }\n\n const topology = await this._loadTopology()\n if (serial !== this._renderSerial) {\n return\n }\n\n const statesLayer = topology.objects['states'] as GeometryCollection\n const countiesLayer = topology.objects['counties'] as GeometryCollection\n\n const states = topoFeature(\n topology,\n statesLayer,\n ) as FeatureCollection<Geometry>\n\n const stateFeature = states.features.find(\n (d) => Number(d.id) === Number(state),\n )\n if (!stateFeature) {\n return\n }\n\n const projection = geoAlbers()\n const path: GeoPath = geoPath().projection(projection)\n\n projection.scale(1).translate([0, 0])\n const b = path.bounds(stateFeature)\n const s =\n 0.95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)\n const t: [number, number] = [\n (width - s * (b[1][0] + b[0][0])) / 2,\n (height - s * (b[1][1] + b[0][1])) / 2,\n ]\n projection.scale(s).translate(t)\n\n const svg: Selection<SVGSVGElement, unknown, null, undefined> = select(host)\n .append('svg')\n .attr('width', width)\n .attr('height', height)\n\n svg\n .append('path')\n .datum(topoMesh(topology, statesLayer, (a, b1) => a !== b1))\n .attr('class', 'mesh')\n .attr('d', path as unknown as string)\n\n svg\n .append('path')\n .datum(stateFeature)\n .attr('class', 'outline')\n .attr('d', path as unknown as string)\n .attr('id', 'land')\n\n svg\n .append('clipPath')\n .attr('id', 'clip-land')\n .append('use')\n .attr('xlink:href', '#land')\n\n const counties = topoFeature(\n topology,\n countiesLayer,\n ) as FeatureCollection<Geometry>\n\n const stateNum = `${parseInt(state, 10)}`\n const stateCounties = counties.features.filter(\n (d) => stateIdFromCountyId(d.id as string | number) === stateNum,\n )\n\n const isInteractive = this.interactive()\n\n const countyPaths = svg\n .selectAll<SVGPathElement, Feature<Geometry>>('path[county-id]')\n .data(stateCounties)\n .enter()\n .append('path')\n .attr('d', path as unknown as string)\n .attr('county-id', (d) => `${d.id}`.padStart(5, '0'))\n\n if (isInteractive) {\n countyPaths\n .on('click', (_event, d) => {\n this.countyClick.emit({\n id: `${d.id}`.padStart(5, '0'),\n feature: d,\n })\n })\n .on('mouseenter', (_event: MouseEvent, d) => {\n select(_event.currentTarget as SVGPathElement).classed(\n 'county-hover',\n true,\n )\n this.countyEnter.emit({\n id: `${d.id}`.padStart(5, '0'),\n feature: d,\n })\n })\n .on('mouseleave', (_event: MouseEvent, d) => {\n select(_event.currentTarget as SVGPathElement).classed(\n 'county-hover',\n false,\n )\n this.countyLeave.emit({\n id: `${d.id}`.padStart(5, '0'),\n feature: d,\n })\n })\n }\n\n this._updateSelectedCounties()\n }\n\n private _updateSelectedCounties(): void {\n const host = this._wrapper().nativeElement\n const selected = this.selectedCountyIds()\n select(host)\n .select<SVGSVGElement>('svg')\n .selectAll<SVGPathElement, Feature<Geometry>>('path[county-id]')\n .attr('class', (d) =>\n isCountySelected(d.id as string | number, selected)\n ? 'county-selected'\n : 'county',\n )\n }\n}\n","import { ComponentHarness, TestElement } from '@angular/cdk/testing'\n\n/**\n * Harness for `TheSeamStatesCountiesMapComponent`. Designed for use in both\n * TestBed and Storybook (via `@marklb/storybook-harness`).\n */\nexport class TheSeamStatesCountiesMapHarness extends ComponentHarness {\n static hostSelector = 'seam-states-counties-map'\n\n private readonly _wrapper = this.locatorFor('.states-counties-map-wrapper')\n private readonly _svg = this.locatorForOptional('svg')\n\n /** Whether the SVG has been rendered yet. */\n async hasRendered(): Promise<boolean> {\n return (await this._svg()) !== null\n }\n\n /** Returns all county path elements that have been rendered. */\n async getCountyPaths(): Promise<TestElement[]> {\n return this.locatorForAll('path[county-id]')()\n }\n\n /** Returns the path for a single county by its FIPS id, or null. */\n async getCountyPath(countyId: string): Promise<TestElement | null> {\n const matches = await this.locatorForAll(`path[county-id=\"${countyId}\"]`)()\n return matches[0] ?? null\n }\n\n /** Ids of every county currently marked `county-selected`. */\n async getSelectedCountyIds(): Promise<string[]> {\n const paths = await this.locatorForAll('path.county-selected')()\n const ids = await Promise.all(paths.map((p) => p.getAttribute('county-id')))\n return ids.filter((id): id is string => id !== null)\n }\n\n /** Click a county by FIPS id. Throws if the county is not rendered. */\n async clickCounty(countyId: string): Promise<void> {\n const path = await this.getCountyPath(countyId)\n if (!path) {\n throw new Error(\n `TheSeamStatesCountiesMapHarness.clickCounty: county ${countyId} is not rendered`,\n )\n }\n await path.click()\n }\n\n /** Simulate pointer entering a county area. Throws if the county is not rendered. */\n async enterCounty(countyId: string): Promise<void> {\n const path = await this.getCountyPath(countyId)\n if (!path) {\n throw new Error(\n `TheSeamStatesCountiesMapHarness.enterCounty: county ${countyId} is not rendered`,\n )\n }\n await path.hover()\n }\n\n /** Simulate pointer leaving a county area by hovering away from it. */\n async leaveCounty(countyId: string): Promise<void> {\n const path = await this.getCountyPath(countyId)\n if (!path) {\n throw new Error(\n `TheSeamStatesCountiesMapHarness.leaveCounty: county ${countyId} is not rendered`,\n )\n }\n // Move pointer to the wrapper (outside any county) to trigger mouseleave.\n await (await this.locatorFor('.states-counties-map-wrapper')()).hover()\n }\n\n /** Rendered viewport dimensions, for layout-sensitive assertions. */\n async getWrapperSize(): Promise<{ width: number; height: number }> {\n const rect = await (await this._wrapper()).getDimensions()\n return { width: rect.width, height: rect.height }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["topoFeature","topoMesh"],"mappings":"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"}
|
package/graphql/index.d.ts
CHANGED
|
@@ -8,8 +8,10 @@ import * as apollo_angular from 'apollo-angular';
|
|
|
8
8
|
import { QueryRef, WatchQueryOptions, Apollo } from 'apollo-angular';
|
|
9
9
|
import { DataFilterState } from '@theseam/ui-common/data-filters';
|
|
10
10
|
import * as i0 from '@angular/core';
|
|
11
|
-
import { InjectionToken,
|
|
11
|
+
import { InjectionToken, Type, Provider, EventEmitter } from '@angular/core';
|
|
12
12
|
import { OperationVariables } from '@apollo/client/core/types';
|
|
13
|
+
import * as _fortawesome_fontawesome_common_types from '@fortawesome/fontawesome-common-types';
|
|
14
|
+
import { IDataExporter } from '@theseam/ui-common/data-exporter';
|
|
13
15
|
import * as _theseam_ui_common_graphql from '@theseam/ui-common/graphql';
|
|
14
16
|
|
|
15
17
|
interface LogQueryLinkOptions {
|
|
@@ -55,17 +57,50 @@ declare function logQueryLink(inner: ApolloLink, options?: LogQueryLinkOptions):
|
|
|
55
57
|
* variable is not referenced anywhere in the (possibly already-transformed)
|
|
56
58
|
* query body.
|
|
57
59
|
* - `variables.inline`: inline named variables into the query AST.
|
|
60
|
+
* - `variables.orderTiebreaker`: append a fallback sort field for deterministic
|
|
61
|
+
* pagination.
|
|
58
62
|
*
|
|
59
63
|
* Hints are applied first, then config-based processing.
|
|
60
64
|
*/
|
|
61
65
|
declare const queryProcessingLink: ApolloLink;
|
|
62
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Checks whether a GraphQL document or value node contains a reference to
|
|
69
|
+
* the named variable.
|
|
70
|
+
*
|
|
71
|
+
* This includes references inside variable definitions — a variable that is
|
|
72
|
+
* defined but not used as an argument will still be found. This behavior is
|
|
73
|
+
* relied on by `removeIfNotUsed` processing to avoid removing variables that
|
|
74
|
+
* may be needed after a later inline step.
|
|
75
|
+
*/
|
|
63
76
|
declare function containsVariable(node: DocumentNode | ValueNode, variableName: string): boolean;
|
|
64
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Creates a variable reference marker for use in filter objects that will be
|
|
80
|
+
* passed to `toGQL` and inlined into a query.
|
|
81
|
+
*
|
|
82
|
+
* When `toGQL` encounters an object with a `gqlVar` property, it emits the
|
|
83
|
+
* value as-is (e.g. `$search`), producing a GraphQL variable reference in the
|
|
84
|
+
* output rather than a string literal.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const filter = { name: { contains: gqlVar('search') } }
|
|
89
|
+
* // toGQL(filter) → '{name: {contains: $search}}'
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
65
92
|
declare function gqlVar(varName: string): {
|
|
66
93
|
gqlVar: string;
|
|
67
94
|
};
|
|
68
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Replaces all references to a variable in the query body with a literal
|
|
98
|
+
* value. If the value represents `undefined`, the variable references are
|
|
99
|
+
* removed from the query instead.
|
|
100
|
+
*
|
|
101
|
+
* This does not remove the variable definition — use `removeVariableDefinition`
|
|
102
|
+
* separately if needed.
|
|
103
|
+
*/
|
|
69
104
|
declare function inlineVariable(query: DocumentNode$1, variableName: string, variableValue: ValueNode$1): DocumentNode$1;
|
|
70
105
|
|
|
71
106
|
declare function parseComments(ast: DocumentNode): Token[];
|
|
@@ -105,15 +140,48 @@ interface HintDefinition {
|
|
|
105
140
|
}
|
|
106
141
|
|
|
107
142
|
declare const DEFAULT_TO_REMOVE_ON_UNDEFINED: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Configuration for how query variables are transformed before a GraphQL
|
|
145
|
+
* operation is sent. Used via `queryProcessingConfig` in Apollo context or
|
|
146
|
+
* passed directly to `processGql`.
|
|
147
|
+
*/
|
|
108
148
|
interface QueryProcessingVariablesConfig {
|
|
149
|
+
/**
|
|
150
|
+
* Variable names to remove from the query when their value is `null` or
|
|
151
|
+
* `undefined`. Removes both the variable definition and argument references.
|
|
152
|
+
*/
|
|
109
153
|
removeIfNotDefined?: string[];
|
|
154
|
+
/**
|
|
155
|
+
* Variable names whose definitions should be removed if the variable is not
|
|
156
|
+
* referenced anywhere in the query. A variable that still has its own
|
|
157
|
+
* definition is considered "present" and will not be removed — this is
|
|
158
|
+
* intentional because the variable may be needed after a later processing
|
|
159
|
+
* step (e.g. `$search` referenced inside a `$where` value that will be
|
|
160
|
+
* inlined).
|
|
161
|
+
*/
|
|
110
162
|
removeIfNotUsed?: string[];
|
|
163
|
+
/**
|
|
164
|
+
* Variable names to inline into the query. The variable's value is converted
|
|
165
|
+
* to a GraphQL literal and substituted directly into the query AST. The
|
|
166
|
+
* variable is then removed from both the definition and the variables map.
|
|
167
|
+
*/
|
|
111
168
|
inline?: string[];
|
|
169
|
+
/**
|
|
170
|
+
* If set, the order variable (expected to be an array of sort objects) will be
|
|
171
|
+
* checked and if this field name is not already present as a sort key, it will
|
|
172
|
+
* be appended with a default direction of DESC. This ensures deterministic
|
|
173
|
+
* ordering for paginated results.
|
|
174
|
+
*/
|
|
175
|
+
orderTiebreaker?: string;
|
|
112
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Top-level configuration for query processing. Passed via Apollo context
|
|
179
|
+
* under the key `queryProcessingConfig`, or to `processGql` directly.
|
|
180
|
+
*/
|
|
113
181
|
interface QueryProcessingConfig {
|
|
114
182
|
variables: QueryProcessingVariablesConfig;
|
|
115
183
|
/**
|
|
116
|
-
*
|
|
184
|
+
* When enabled, paging is disabled and all records are returned.
|
|
117
185
|
*/
|
|
118
186
|
disablePaging?: boolean;
|
|
119
187
|
}
|
|
@@ -153,16 +221,62 @@ declare function createHintsToken(token: Token$1, node: HintsToken['node'], kind
|
|
|
153
221
|
declare function getHintsToken(token: Token$1, ast: DocumentNode$1): HintsToken | null;
|
|
154
222
|
declare function parseHints(ast: DocumentNode$1): HintsToken[];
|
|
155
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Removes a variable's definition from the query (e.g. `$where: String` in the
|
|
226
|
+
* operation signature). Does not remove argument references to that variable in
|
|
227
|
+
* the query body.
|
|
228
|
+
*
|
|
229
|
+
* To remove both the definition and argument references, use `removeVariable`.
|
|
230
|
+
*/
|
|
156
231
|
declare function removeVariableDefinition(query: DocumentNode$1, variableName: string): DocumentNode$1;
|
|
157
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Removes variable definitions from the specified operation when the variable
|
|
235
|
+
* does not have a corresponding value in the provided variables object (i.e.
|
|
236
|
+
* the value is `null` or `undefined`).
|
|
237
|
+
*
|
|
238
|
+
* Unlike `removeVariable`, this only removes the definitions — it does not
|
|
239
|
+
* remove argument references in the query body.
|
|
240
|
+
*/
|
|
158
241
|
declare function removeVariableDefinitionsNotDefined(query: DocumentNode$1, node: OperationDefinitionNode, variables: Operation['variables']): DocumentNode$1;
|
|
159
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Removes a variable from the query by removing both the variable definition
|
|
245
|
+
* (e.g. `$where: String` in the operation signature) and argument references
|
|
246
|
+
* with the same name (e.g. `where: $where` in the field arguments).
|
|
247
|
+
*
|
|
248
|
+
* To remove only the definition without touching arguments, use
|
|
249
|
+
* `removeVariableDefinition`.
|
|
250
|
+
*/
|
|
160
251
|
declare function removeVariable(query: DocumentNode$1, variableName: string): DocumentNode$1;
|
|
161
252
|
|
|
162
253
|
declare function toGQL(json: any): string;
|
|
163
254
|
|
|
164
255
|
declare function hintsTokensContainingHint(hintsTokens: HintsToken[], hint: string): HintsToken[];
|
|
165
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Transforms a GraphQL query and its variables according to the provided
|
|
259
|
+
* processing configuration.
|
|
260
|
+
*
|
|
261
|
+
* Two mechanisms are supported and can be combined freely:
|
|
262
|
+
*
|
|
263
|
+
* **Hint-based** — `# @gql-hint: <name>` comments in the query.
|
|
264
|
+
* - `remove-not-defined`: removes variables whose value is null/undefined.
|
|
265
|
+
* - `inline-variable`: substitutes variable values directly into the AST.
|
|
266
|
+
*
|
|
267
|
+
* **Config-based** — via `QueryProcessingConfig`:
|
|
268
|
+
* - `removeIfNotDefined`: remove named variables when null/undefined.
|
|
269
|
+
* - `removeIfNotUsed`: remove named variable definitions when unreferenced.
|
|
270
|
+
* - `inline`: inline named variables into the query AST.
|
|
271
|
+
* - `orderTiebreaker`: append a fallback sort field for deterministic pagination.
|
|
272
|
+
*
|
|
273
|
+
* Hints are applied first, then config-based processing, then cleanup.
|
|
274
|
+
*/
|
|
275
|
+
declare function processGql(query: DocumentNode$1, variables: Record<string, any>, queryProcessingConfig: QueryProcessingConfig): {
|
|
276
|
+
query: DocumentNode$1;
|
|
277
|
+
variables: Record<string, any>;
|
|
278
|
+
};
|
|
279
|
+
|
|
166
280
|
interface DatatableGraphQLDataMapperResult<TRow = EmptyObject> {
|
|
167
281
|
rows: TRow[];
|
|
168
282
|
/**
|
|
@@ -239,6 +353,20 @@ declare class DatatableGraphQLQueryRef<TData, TVariables extends DatatableGraphQ
|
|
|
239
353
|
* Default error handler used when no subscriber is listening to `error$`.
|
|
240
354
|
*/
|
|
241
355
|
_defaultErrorHandler?: DatatableGraphQLErrorHandler | undefined);
|
|
356
|
+
/**
|
|
357
|
+
* Returns an observable of mapped rows from the query result.
|
|
358
|
+
*
|
|
359
|
+
* The mapper transforms the raw GraphQL response data into the row format
|
|
360
|
+
* the datatable expects, and provides the total count for pagination.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* this.rows$ = this._queryRef.rows((data) => ({
|
|
365
|
+
* rows: data.items.items,
|
|
366
|
+
* totalCount: data.items.totalCount,
|
|
367
|
+
* }))
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
242
370
|
rows(mapper: DatatableGraphQLDataMapper<TData, TRow>): Observable<TRow[]>;
|
|
243
371
|
private _rowsObservable;
|
|
244
372
|
/**
|
|
@@ -446,6 +574,60 @@ declare const mapSearchNumericColumnsDataFilterStateToGql: (filterState: Columns
|
|
|
446
574
|
|
|
447
575
|
declare const mapSearchTextColumnsDataFilterStateToGql: (filterState: ColumnsDataFilterState<TheSeamColumnsDataFilterTextSearchFormState>, context: MapperContext<any>) => FilterStateMapperResult;
|
|
448
576
|
|
|
577
|
+
interface DatatableExportPayload {
|
|
578
|
+
graphQlQuery: {
|
|
579
|
+
queryString: string;
|
|
580
|
+
queryVariables: Record<string, any>;
|
|
581
|
+
operationName: string;
|
|
582
|
+
columns: TheSeamDatatableColumn[];
|
|
583
|
+
};
|
|
584
|
+
exportType: string;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Abstract transport for server-side datatable exports.
|
|
588
|
+
*
|
|
589
|
+
* THIS SHOULD BE IMPLEMENTED AND PROVIDED BY THE APPLICATION USING 'ui-common'.
|
|
590
|
+
*
|
|
591
|
+
* Example:
|
|
592
|
+
* ```ts
|
|
593
|
+
* bootstrapApplication(AppComponent, {
|
|
594
|
+
* providers: [
|
|
595
|
+
* provideDatatableExportTransport(AppDatatableExportTransport),
|
|
596
|
+
* ],
|
|
597
|
+
* })
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
600
|
+
declare abstract class DatatableExportTransport {
|
|
601
|
+
abstract export(data: DatatableExportPayload): Observable<File>;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Register the application's `DatatableExportTransport` implementation.
|
|
605
|
+
*
|
|
606
|
+
* ```ts
|
|
607
|
+
* bootstrapApplication(AppComponent, {
|
|
608
|
+
* providers: [
|
|
609
|
+
* provideDatatableExportTransport(AppDatatableExportTransport),
|
|
610
|
+
* ],
|
|
611
|
+
* })
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
declare function provideDatatableExportTransport(transportType: Type<DatatableExportTransport>): Provider;
|
|
615
|
+
|
|
616
|
+
declare class DatatableGqlDataExporter<TData, TVariables extends DatatableGraphQLVariables = EmptyObject, TRow = EmptyObject> implements IDataExporter {
|
|
617
|
+
private readonly _exporterName;
|
|
618
|
+
private readonly _exporterLabel;
|
|
619
|
+
private readonly _columns;
|
|
620
|
+
private readonly _queryRef;
|
|
621
|
+
private readonly _transport;
|
|
622
|
+
private readonly _exportType;
|
|
623
|
+
get name(): string;
|
|
624
|
+
get label(): string;
|
|
625
|
+
icon: _fortawesome_fontawesome_common_types.IconDefinition;
|
|
626
|
+
readonly skipDataMapping: boolean;
|
|
627
|
+
constructor(_exporterName: string, _exporterLabel: string, _columns: TheSeamDatatableColumn[], _queryRef: DatatableGraphQLQueryRef<TData, TVariables, TRow>, _transport: DatatableExportTransport, _exportType: string);
|
|
628
|
+
export<T>(data: T[]): Observable<boolean>;
|
|
629
|
+
}
|
|
630
|
+
|
|
449
631
|
/**
|
|
450
632
|
* Inlines the variable's current value directly into the query AST and removes
|
|
451
633
|
* the variable definition from the parameter list. The variable is also
|
|
@@ -615,5 +797,5 @@ interface SimpleGqlTestVariables extends SimpleGqlTestExtraVariables {
|
|
|
615
797
|
search?: string;
|
|
616
798
|
}
|
|
617
799
|
|
|
618
|
-
export { DATATABLE_GRAPHQL_SERVICE_CONFIG, DEFAULT_PAGE_SIZE, DEFAULT_TO_REMOVE_ON_UNDEFINED, DatatableGraphQLQueryRef, DatatableGraphqlService, GQLDirection, GQLVariable, HINT_NAMES_CAPTURE_REGEX, HINT_PREFIX_REGEX, HintsKind, MAX_ERROR_RECOVERY_ATTEMPTS, MockDatatable, SIMPLE_GQL_TEST_QUERY, SIMPLE_GQL_TEST_SEARCH_QUERY, baseSchemaFragment, checkRecordsHaveValue, containsVariable, createHintsToken, createMockApolloTestingProvider, createSimpleGqlTestRecord, createSimpleGqlTestRoot, createSortsMapper, filterWhere, filteredResults, getHintsToken, getPageInfo, getTokenAppliesTo, gqlVar, hintNamesFromHintToken, hintsTokensContainingHint, inlineVariable, inlineVariableHintDef, inlineVariableTransformer, isCommentToken, isHintToken, isInlineComment, logQueryLink, mapFilterStates, mapSearchDateColumnsDataFilterStateToGql, mapSearchNumericColumnsDataFilterStateToGql, mapSearchTextColumnsDataFilterStateToGql, mockGraphQLLink, observeRowsWithGqlInputsHandling, parseAst, parseComments, parseHints, queryProcessingLink, removeNotDefinedHintDef, removeNotDefinedTransformer, removeVariable, removeVariableDefinition, removeVariableDefinitionsNotDefined, skipAndTake, sortItems, toGQL };
|
|
619
|
-
export type { CreateSortsMapperOptions, DatatableGraphQLDataMapper, DatatableGraphQLDataMapperResult, DatatableGraphQLErrorHandler, DatatableGraphQLVariables, DatatableGraphqlServiceConfig, DatatableQueryOptions, EmptyObject, FilterStateMapper, FilterStateMapperFilter, FilterStateMapperResult, FilterStateMapperVariables, FilterStateMappers, FilteredResults, FilteredResultsPageInfo, GqlDatatableAccessor, HintDefinition, HintTransformOperation, HintTransformer, HintsToken, LogQueryLinkOptions, MapperContext, MockApolloTestingProviderOptions, MockGraphQLLinkOptions, QueryProcessingConfig, QueryProcessingVariablesConfig, SimpleGqlTestExtraVariables, SimpleGqlTestRecord, SimpleGqlTestVariables, SortClause, SortsMapper, SortsMapperFieldEntry, SortsMapperFieldMap, SortsMapperResult, TypedFilterInput, TypedFilterStateMapperResult, WhereArg };
|
|
800
|
+
export { DATATABLE_GRAPHQL_SERVICE_CONFIG, DEFAULT_PAGE_SIZE, DEFAULT_TO_REMOVE_ON_UNDEFINED, DatatableExportTransport, DatatableGqlDataExporter, DatatableGraphQLQueryRef, DatatableGraphqlService, GQLDirection, GQLVariable, HINT_NAMES_CAPTURE_REGEX, HINT_PREFIX_REGEX, HintsKind, MAX_ERROR_RECOVERY_ATTEMPTS, MockDatatable, SIMPLE_GQL_TEST_QUERY, SIMPLE_GQL_TEST_SEARCH_QUERY, baseSchemaFragment, checkRecordsHaveValue, containsVariable, createHintsToken, createMockApolloTestingProvider, createSimpleGqlTestRecord, createSimpleGqlTestRoot, createSortsMapper, filterWhere, filteredResults, getHintsToken, getPageInfo, getTokenAppliesTo, gqlVar, hintNamesFromHintToken, hintsTokensContainingHint, inlineVariable, inlineVariableHintDef, inlineVariableTransformer, isCommentToken, isHintToken, isInlineComment, logQueryLink, mapFilterStates, mapSearchDateColumnsDataFilterStateToGql, mapSearchNumericColumnsDataFilterStateToGql, mapSearchTextColumnsDataFilterStateToGql, mockGraphQLLink, observeRowsWithGqlInputsHandling, parseAst, parseComments, parseHints, processGql, provideDatatableExportTransport, queryProcessingLink, removeNotDefinedHintDef, removeNotDefinedTransformer, removeVariable, removeVariableDefinition, removeVariableDefinitionsNotDefined, skipAndTake, sortItems, toGQL };
|
|
801
|
+
export type { CreateSortsMapperOptions, DatatableExportPayload, DatatableGraphQLDataMapper, DatatableGraphQLDataMapperResult, DatatableGraphQLErrorHandler, DatatableGraphQLVariables, DatatableGraphqlServiceConfig, DatatableQueryOptions, EmptyObject, FilterStateMapper, FilterStateMapperFilter, FilterStateMapperResult, FilterStateMapperVariables, FilterStateMappers, FilteredResults, FilteredResultsPageInfo, GqlDatatableAccessor, HintDefinition, HintTransformOperation, HintTransformer, HintsToken, LogQueryLinkOptions, MapperContext, MockApolloTestingProviderOptions, MockGraphQLLinkOptions, QueryProcessingConfig, QueryProcessingVariablesConfig, SimpleGqlTestExtraVariables, SimpleGqlTestRecord, SimpleGqlTestVariables, SortClause, SortsMapper, SortsMapperFieldEntry, SortsMapperFieldMap, SortsMapperResult, TypedFilterInput, TypedFilterStateMapperResult, WhereArg };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theseam/ui-common",
|
|
3
|
-
"version": "1.0.2-beta.
|
|
3
|
+
"version": "1.0.2-beta.57",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/cdk": "^20.2.3",
|
|
6
6
|
"@angular/common": "^20.3.0",
|
|
@@ -267,6 +267,10 @@
|
|
|
267
267
|
"types": "./shared/index.d.ts",
|
|
268
268
|
"default": "./fesm2022/theseam-ui-common-shared.mjs"
|
|
269
269
|
},
|
|
270
|
+
"./states-counties-map": {
|
|
271
|
+
"types": "./states-counties-map/index.d.ts",
|
|
272
|
+
"default": "./fesm2022/theseam-ui-common-states-counties-map.mjs"
|
|
273
|
+
},
|
|
270
274
|
"./storage": {
|
|
271
275
|
"types": "./storage/index.d.ts",
|
|
272
276
|
"default": "./fesm2022/theseam-ui-common-storage.mjs"
|