@bicharts/chart-host 0.6.83 → 0.6.85
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 +43 -0
- package/dist/adapters.mjs +10 -4
- package/dist/card.mjs +2 -2
- package/dist/{chunk-2XWE6YFO.mjs → chunk-2ZUUIZWL.mjs} +115 -115
- package/dist/chunk-7HKVV5T5.mjs +198 -0
- package/dist/chunk-BQFG5HWZ.mjs +582 -0
- package/dist/{chunk-CJNONH6V.mjs → chunk-CDWNZHIN.mjs} +75 -592
- package/dist/{chunk-XIAL6EEC.mjs → chunk-DTOGTKAJ.mjs} +10 -2
- package/dist/{chunk-4KCSAHL5.mjs → chunk-GG4U66E4.mjs} +9 -9
- package/dist/{chunk-MGAO2DR7.mjs → chunk-IURROJT7.mjs} +95 -193
- package/dist/{chunk-7JWL6LYE.mjs → chunk-MR6TL7QZ.mjs} +1 -1
- package/dist/chunk-QIRLWJBI.mjs +665 -0
- package/dist/chunk-ZGQURBRJ.mjs +265 -0
- package/dist/index.mjs +148 -285
- package/dist/legend-fit.mjs +1613 -0
- package/dist/page.mjs +4 -1
- package/dist/react.mjs +8 -5
- package/dist/testing.mjs +1 -1
- package/dist/types/contract.d.ts +35 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/lasso.d.ts +143 -0
- package/dist/types/legendFit/frameFit.d.ts +89 -0
- package/dist/types/legendFit/frameFitDom.d.ts +70 -0
- package/dist/types/legendFit/index.d.ts +4 -0
- package/dist/types/legendFit/legendReconcile.d.ts +295 -0
- package/dist/types/legendFit/legendReconcileDom.d.ts +109 -0
- package/dist/types/qualifyGroups.d.ts +61 -3
- package/dist/types/selection.d.ts +51 -0
- package/package.json +6 -1
package/dist/page.mjs
CHANGED
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
readControls,
|
|
15
15
|
viewFromToken,
|
|
16
16
|
viewToken
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-DTOGTKAJ.mjs";
|
|
18
|
+
import "./chunk-QIRLWJBI.mjs";
|
|
19
|
+
import "./chunk-IURROJT7.mjs";
|
|
20
|
+
import "./chunk-MR6TL7QZ.mjs";
|
|
18
21
|
import "./chunk-GHODWOAL.mjs";
|
|
19
22
|
export {
|
|
20
23
|
LINKED_HOVER_CLASS,
|
package/dist/react.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
payloadRowReader,
|
|
14
14
|
viewFromToken,
|
|
15
15
|
viewToken
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DTOGTKAJ.mjs";
|
|
17
17
|
import {
|
|
18
18
|
assembleD3,
|
|
19
19
|
createChartGroup,
|
|
@@ -22,11 +22,14 @@ import {
|
|
|
22
22
|
loadGeo,
|
|
23
23
|
syncMemberSelection,
|
|
24
24
|
toSourceRows
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-CDWNZHIN.mjs";
|
|
26
|
+
import "./chunk-QIRLWJBI.mjs";
|
|
27
|
+
import "./chunk-2ZUUIZWL.mjs";
|
|
28
|
+
import "./chunk-7HKVV5T5.mjs";
|
|
29
|
+
import "./chunk-IURROJT7.mjs";
|
|
30
|
+
import "./chunk-BQFG5HWZ.mjs";
|
|
28
31
|
import "./chunk-A2GMXZP7.mjs";
|
|
29
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-MR6TL7QZ.mjs";
|
|
30
33
|
import "./chunk-GHODWOAL.mjs";
|
|
31
34
|
|
|
32
35
|
// src/react.tsx
|
package/dist/testing.mjs
CHANGED
package/dist/types/contract.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const HOST_CONTRACT_VERSION = "1.
|
|
1
|
+
export declare const HOST_CONTRACT_VERSION = "1.16.0";
|
|
2
2
|
export type GeoPointPrecision = "latlon" | "city" | "zip3" | "state" | "country";
|
|
3
3
|
/** Every valid tier, ordered most precise → coarsest. Runtime form of GeoPointPrecision. */
|
|
4
4
|
export declare const GEO_POINT_PRECISIONS: readonly GeoPointPrecision[];
|
|
@@ -19,6 +19,18 @@ export declare const MARK_KEY_ATTR = "data-mark-key";
|
|
|
19
19
|
export declare const NO_ROW_MARKS_ATTR = "data-lch-no-row-marks";
|
|
20
20
|
export declare const SNAPSHOT_SVG_ATTR = "data-lch-snapshot";
|
|
21
21
|
export declare const XFILTER_REFRESH_EVENT = "llm-xfilter-refresh";
|
|
22
|
+
/** What the event carries. `source` names who selected ("user" when a reader's gesture did); the host
|
|
23
|
+
* passes it on to its subscribers, and "chart" stands in when it is absent. */
|
|
24
|
+
export type XfilterRefreshDetail = {
|
|
25
|
+
clear: true;
|
|
26
|
+
source?: string;
|
|
27
|
+
} | {
|
|
28
|
+
mark: Element;
|
|
29
|
+
source?: string;
|
|
30
|
+
} | {
|
|
31
|
+
rows: readonly number[];
|
|
32
|
+
source?: string;
|
|
33
|
+
};
|
|
22
34
|
export declare const CONTAINER_SLOT_ANIM_STOP = "__llmAnimStop";
|
|
23
35
|
export declare const CONTAINER_SLOT_XF_CLEAR = "__llmXfClear";
|
|
24
36
|
export declare const CONTAINER_SLOT_INITIAL_XF_MARK = "__llmInitialXfMark";
|
|
@@ -109,6 +121,9 @@ export interface RenderOptions {
|
|
|
109
121
|
rolesRefused?: string[];
|
|
110
122
|
} | null;
|
|
111
123
|
dateCellsAreUtcDays?: boolean;
|
|
124
|
+
dataComplete?: boolean;
|
|
125
|
+
rowsWithheld?: number;
|
|
126
|
+
rowsFilteredOut?: number;
|
|
112
127
|
geoPointDest?: {
|
|
113
128
|
precision: GeoPointPrecision | null;
|
|
114
129
|
precisionCounts: Record<GeoPointPrecision, number>;
|
|
@@ -178,6 +193,25 @@ export interface RenderOptions {
|
|
|
178
193
|
* take the reader's gesture with it: the helper swallows anything this raises.
|
|
179
194
|
*/
|
|
180
195
|
onControlChange?: (e: ChartControlChange) => void;
|
|
196
|
+
/**
|
|
197
|
+
* THE HOST LASSO (contract 1.16.0): whether this chart gets the gesture that draws around marks and selects the
|
|
198
|
+
* ones inside, and what the host does when it ends. A chart never draws the gesture; it hears it as an
|
|
199
|
+
* `llm-lasso` event on its container.
|
|
200
|
+
* capable - the capability the SERVER sent with this chart (the chart's type declares that it has no drag
|
|
201
|
+
* gesture of its own and that a lasso means something for it). ABSENT MEANS NOT CAPABLE: no host
|
|
202
|
+
* holds a list of chart types, so a chart saved before the capability existed has none.
|
|
203
|
+
* enabled - the reader's setting; on unless it is exactly false. Both must hold for a surface to be drawn.
|
|
204
|
+
* rowCap - the most rows in view that still get a lasso; above it the whole lasso is off and a caption says
|
|
205
|
+
* so. Absent is LASSO_ROW_CAP (provisional).
|
|
206
|
+
* actions - what the host runs on release, in order, by name. Absent is ["select"]; a name the host does
|
|
207
|
+
* not know is skipped, so a list can be sent ahead of the build that understands it.
|
|
208
|
+
*/
|
|
209
|
+
lasso?: {
|
|
210
|
+
enabled?: boolean;
|
|
211
|
+
rowCap?: number;
|
|
212
|
+
actions?: string[];
|
|
213
|
+
capable?: boolean;
|
|
214
|
+
};
|
|
181
215
|
}
|
|
182
216
|
/** One reader gesture on a chart's own control. */
|
|
183
217
|
export interface ChartControlChange {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { HOST_CONTRACT_VERSION, GEO_POINT_PRECISIONS, MARK_CLASS, LEGEND_MARK_CLASS, AXIS_FILTER_CLASS, ROW_IDX_ATTR, LIFT_SELECTED_CLASS, CONTROL_CLASS, NO_ROW_MARKS_ATTR, SELECTION_RULE_ATTR, SELECTION_RULE_CLICKED_MARKS, MARK_KEY_ATTR, CONTAINER_SLOT_CLICKED_MARKS, SNAPSHOT_SVG_ATTR, XFILTER_REFRESH_EVENT, CONTAINER_SLOT_ANIM_STOP, CONTAINER_SLOT_XF_CLEAR, CONTAINER_SLOT_INITIAL_XF_MARK, CONTAINER_SLOT_UI_STATE, VIEW_ONLY_UI_STATE_KEYS, withoutViewOnlyKeys, HOST_CONTAINER_CLASS, SELECTION_ACTIVE_CLASS, MARK_SELECTED_CLASS, ACTIVE_TICK_CLASS, DIM_OPACITY_VAR, DIM_OPACITY_DEFAULT, chartOwnsTimeline, periodTickSuppressesFeedback, ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX, ANIM_LOOP_DELAY_DEFAULT, ANIM_LOOP_DELAY_MIN, ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX, ANIMATION_OPTION_KEYS, COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX, FLIP_MODE_DEFAULT, FLIP_INTERVAL_MIN_MS, FLIP_INTERVAL_MAX_MS, FLIP_DIRECTION_DEFAULT, APPROXIMATE_POSITIONS_DEFAULT, VALUE_AXIS_BASELINE_DEFAULT, SEASONAL_MARKERS_DEFAULT, MAX_MAP_POINTS_DEFAULT, type GeoPointPrecision, type GeoMapKind, type TimelineStyle, type FlipMode, type FlipDirection, type ApproximatePositions, type ValueAxisBaseline, type SeasonalMarkers, type ColorScaleScope, type RenderOptions, type ViewStateProvider, type ChartControlChange, } from "./contract";
|
|
1
|
+
export { HOST_CONTRACT_VERSION, GEO_POINT_PRECISIONS, MARK_CLASS, LEGEND_MARK_CLASS, AXIS_FILTER_CLASS, ROW_IDX_ATTR, LIFT_SELECTED_CLASS, CONTROL_CLASS, NO_ROW_MARKS_ATTR, SELECTION_RULE_ATTR, SELECTION_RULE_CLICKED_MARKS, MARK_KEY_ATTR, CONTAINER_SLOT_CLICKED_MARKS, SNAPSHOT_SVG_ATTR, XFILTER_REFRESH_EVENT, CONTAINER_SLOT_ANIM_STOP, CONTAINER_SLOT_XF_CLEAR, CONTAINER_SLOT_INITIAL_XF_MARK, CONTAINER_SLOT_UI_STATE, VIEW_ONLY_UI_STATE_KEYS, withoutViewOnlyKeys, HOST_CONTAINER_CLASS, SELECTION_ACTIVE_CLASS, MARK_SELECTED_CLASS, ACTIVE_TICK_CLASS, DIM_OPACITY_VAR, DIM_OPACITY_DEFAULT, chartOwnsTimeline, periodTickSuppressesFeedback, ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX, ANIM_LOOP_DELAY_DEFAULT, ANIM_LOOP_DELAY_MIN, ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX, ANIMATION_OPTION_KEYS, COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX, FLIP_MODE_DEFAULT, FLIP_INTERVAL_MIN_MS, FLIP_INTERVAL_MAX_MS, FLIP_DIRECTION_DEFAULT, APPROXIMATE_POSITIONS_DEFAULT, VALUE_AXIS_BASELINE_DEFAULT, SEASONAL_MARKERS_DEFAULT, MAX_MAP_POINTS_DEFAULT, type GeoPointPrecision, type GeoMapKind, type TimelineStyle, type FlipMode, type FlipDirection, type ApproximatePositions, type ValueAxisBaseline, type SeasonalMarkers, type ColorScaleScope, type RenderOptions, type ViewStateProvider, type ChartControlChange, type XfilterRefreshDetail, } from "./contract";
|
|
2
2
|
export { resolveOptions, type ResolveOptionsInput } from "./defaults";
|
|
3
3
|
export { OPTIONS_VOCABULARY, LIVE_OPTION_KEYS, MAX_MAP_POINTS_MIN, MAX_MAP_POINTS_MAX, LIMIT_COUNT_MAX, wireLimitCount, codeReadsOption, type OptionVocabularyEntry, type OptionTiming, type OptionWire, type OptionKey, } from "./optionsVocabulary";
|
|
4
4
|
export { loadGeo, geoFromCache, registerGeo, registerGeoAsset, geoAssetFor, clearGeoCache, type GeoAssetName, } from "./geoLazy";
|
|
@@ -13,14 +13,15 @@ export type { MarkNoteLike, NoteBadgeOptions } from "./markNotes";
|
|
|
13
13
|
export type { Controls, ControlInfo, ControlsAttachment } from "./controls";
|
|
14
14
|
export { createChartGroup, toSourceRows, syncMemberSelection, type ChartGroup, type ChartGroupMember, type ChartGroupSelection, type ChartGroupChange, type ChartGroupMemberOptions, type ChartGroupSourceOptions, type ChartGroupPayload } from "./group";
|
|
15
15
|
export type { Filter, FilterScope, FilterOptions, FilterKey, FilterChangeReason, FilterBinding, BindFilterOptions, VegaInteractionEvent, PageView } from "./filterScope";
|
|
16
|
-
export { createMarkResolver, isInsideControl, rowIdxsFromMark, nextSelection, selectionRuleOf, markKeyOf, planSelectionPaint, nextClickedMarks, sameRowSet, type SelectionRule, type ClickedMarks, type SelectionPaintMark, type SelectionPaintMode, type SelectionPaintPlan, collectD3GlyphCenters, clearSelectionGlyphs, paintSelectionGlyphs, SELECTION_GLYPH_CLASS, type MarkResolver, type MarkResolverEnv, type GlyphCenter, type SelectionGlyphStyle, type SelectionModifiers, } from "./selection";
|
|
16
|
+
export { createMarkResolver, isInsideControl, rowIdxsFromMark, nextSelection, DRAG_NOT_CLICK_PX, gestureWasDrag, trackPress, pressOf, clearPress, clickWasDrag, CONTAINER_SLOT_PRESS_AT, type PressPoint, dispatchRowSet, validRowSet, ROW_SET_MAX, selectionRuleOf, markKeyOf, planSelectionPaint, nextClickedMarks, sameRowSet, type SelectionRule, type ClickedMarks, type SelectionPaintMark, type SelectionPaintMode, type SelectionPaintPlan, collectD3GlyphCenters, clearSelectionGlyphs, paintSelectionGlyphs, SELECTION_GLYPH_CLASS, type MarkResolver, type MarkResolverEnv, type GlyphCenter, type SelectionGlyphStyle, type SelectionModifiers, } from "./selection";
|
|
17
|
+
export { installLasso, clearLasso, lassoVerdict, lassoRowCap, lassoActions, registerLassoAction, pointInShape, LASSO_ROW_CAP, LASSO_DRAG_PX, LASSO_SAMPLE_PX, LASSO_MAX_VERTICES, LASSO_RESTORE_MAX_VERTICES, LASSO_RESTORE_MAX_SHAPES, LASSO_EVENT, LASSO_SURFACE_CLASS, LASSO_OUTLINE_CLASS, LASSO_COUNT_CLASS, LASSO_NOTE_CLASS, LASSO_PLOT_CLASS, LASSO_MODE_ATTR, LASSO_KNOB_KEY, LASSO_AXES_SLOT, LASSO_DEFAULT_ACTIONS, type LassoSetting, type LassoVerdict, type LassoMode, type LassoSpace, type LassoShape, type LassoStored, type LassoEventDetail, type LassoAxes, type LassoAction, type LassoActionContext, type LassoEnv, type LassoHandle, } from "./lasso";
|
|
17
18
|
export type { AdapterEvent, AdapterHit, AdapterId, AdapterScene, MarkHandle, MarkInfo, MarkRole, PaintRequest, RendererAdapter, RowTable, } from "./adapters/types";
|
|
18
19
|
export { planTrivialChart, compileTrivialSource, type TrivialPlan, type TrivialShapeKind } from "./trivial";
|
|
19
20
|
export { captureSvgSnapshot, svgToDataUrl, svgNaturalSize, rasterizeSvgToPngDataUrl, tableHostOf, chartSvgOf, htmlToSvgDataUrl, htmlNaturalSize, type SnapshotOptions } from "./snapshot";
|
|
20
21
|
export { shouldReview, buildReviewWire, bareBase64, actionFor, type ReviewGate, type ReviewWire, type ReviewVerdict, type ReviewAction } from "./review";
|
|
21
22
|
export { askApplyImprovements, type ReviewDialogOptions, type ReviewDialogText } from "./reviewDialog";
|
|
22
23
|
export { mountProductFeedbackBox, normaliseProductFeedbackText, productFeedbackSendable, buildProductFeedbackPayload, PRODUCT_FEEDBACK_MAX_CHARS, PRODUCT_FEEDBACK_MIN_CHARS, type ProductFeedbackBox, type ProductFeedbackBoxOptions, type ProductFeedbackText, type ProductFeedbackSendResult, type ProductFeedbackPayload, type ProductFeedbackIdentity, type ProductFeedbackHost, } from "./productFeedback";
|
|
23
|
-
export { qualifyGroupHeadingFor, newQualifyGroupState, type QualifyGroupRow, type QualifyGroupState, type QualifyGroupHeading, orderRefusalsForDisplay, refusalIsSelectable, hasRefusalsToShow, qualifyRefusalHeadingFor, newQualifyRefusalGroupState, qualifyRefusalReason, QUALIFY_REFUSAL_UNSPECIFIED, type QualifyRefusalRow, type QualifyRefusalGroupState, type QualifyRefusalHeading, } from "./qualifyGroups";
|
|
24
|
+
export { qualifyGroupHeadingFor, newQualifyGroupState, type QualifyGroupRow, type QualifyGroupState, type QualifyGroupHeading, orderRefusalsForDisplay, refusalIsSelectable, hasRefusalsToShow, qualifyRefusalHeadingFor, newQualifyRefusalGroupState, qualifyRefusalReason, QUALIFY_REFUSAL_UNSPECIFIED, refusalIsTileBound, QUALIFY_TILE_REFUSAL_CODES, QUALIFY_TOO_SMALL_HEADING, type QualifyRefusalRow, type QualifyRefusalGroupState, type QualifyRefusalHeading, } from "./qualifyGroups";
|
|
24
25
|
export { qualifyPick, qualifyAuto, qualifyCancel, launchGenerates, launchFavorStyle, chooserFitsViewport, shouldOpenChooserOnGenerate, shouldOpenInlineChooserOnGenerate, canConfirmLaunch, confirmLaunch, qualifyFailureFallsOpen, CHOOSER_MIN_WIDTH_PX, CHOOSER_MIN_HEIGHT_PX, type QualifyLaunchOutcome, type ChooserGateInput, } from "./qualifyLaunch";
|
|
25
26
|
export { normalizeFilterTerm, filterQualifyRows, readQualifyChartRow, readQualifyRefusalRow, DEFAULT_FILTER_MATCH_STYLE, filterMatchStyleOf, type FilterMatchStyle, filterFitsChooser, listNeedsFilter, qualifyFilterGate, inlineFilterGate, FILTER_MIN_TERM_CHARS, FILTER_ROW_PX, FILTER_MIN_WIDTH_PX, FILTER_MIN_HEIGHT_PX, INLINE_FILTER_MIN_ROWS, computeQualifyFilterView, qualifyFilterCountText, type QualifyFilterTier, type QualifyFilterResult, type QualifyFilterRead, type QualifyFilterGateReason, type QualifyFilterGateInput, type QualifyFilterRow, type QualifyFilterGroup, type QualifyFilterView, type QualifyFilterNoteKind, } from "./qualifyFilter";
|
|
26
27
|
export { qualifyTableRows, type QualifyTableRow, type QualifyTableSource } from "./qualifyTable";
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { type RenderOptions } from "./contract";
|
|
2
|
+
/**
|
|
3
|
+
* THE MOST ROWS A CHART MAY HAVE IN VIEW AND STILL OFFER A LASSO.
|
|
4
|
+
*
|
|
5
|
+
* PROVISIONAL. Unmeasured: 5,000 is a stand-in until the time Power BI Desktop takes to apply a selection
|
|
6
|
+
* of that many ids is measured on a release-test page (1,000, 5,000 and 13,000 ids), and that number
|
|
7
|
+
* replaces this one. Do not read it as a finding. It is the worst case that is allowed for, not the
|
|
8
|
+
* typical one: a reader can lasso everything.
|
|
9
|
+
*
|
|
10
|
+
* Above it the WHOLE lasso is off: no surface is installed, and a one-line caption says why. It is one
|
|
11
|
+
* number for every host; a host may pass its own through RenderOptions.lasso.rowCap.
|
|
12
|
+
*/
|
|
13
|
+
export declare const LASSO_ROW_CAP = 5000;
|
|
14
|
+
/** Travel, in px on either axis, at which a press stops being a click and becomes a lasso. */
|
|
15
|
+
export declare const LASSO_DRAG_PX = 5;
|
|
16
|
+
/** A freeform point is kept only when it is this far from the last one kept. */
|
|
17
|
+
export declare const LASSO_SAMPLE_PX = 3;
|
|
18
|
+
/** A freeform shape longer than this is thinned before it is used, so what is published, stored and
|
|
19
|
+
* restored is one and the same shape. */
|
|
20
|
+
export declare const LASSO_MAX_VERTICES = 400;
|
|
21
|
+
/** What a restore will read, per shape and in all: a bag is untrusted, and a poisoned one must not be able
|
|
22
|
+
* to ask for unbounded work. */
|
|
23
|
+
export declare const LASSO_RESTORE_MAX_VERTICES = 2000;
|
|
24
|
+
export declare const LASSO_RESTORE_MAX_SHAPES = 32;
|
|
25
|
+
/** The DOM event a chart listens for on its container. detail is a LassoEventDetail. */
|
|
26
|
+
export declare const LASSO_EVENT = "llm-lasso";
|
|
27
|
+
export declare const LASSO_SURFACE_CLASS = "lch-lasso-surface";
|
|
28
|
+
export declare const LASSO_OUTLINE_CLASS = "lch-lasso-outline";
|
|
29
|
+
export declare const LASSO_COUNT_CLASS = "lch-lasso-count";
|
|
30
|
+
export declare const LASSO_NOTE_CLASS = "lch-lasso-note";
|
|
31
|
+
/** The group a chart draws its plot in; the surface goes over it. Without one the chart's svg is the plot. */
|
|
32
|
+
export declare const LASSO_PLOT_CLASS = "lch-plot";
|
|
33
|
+
/** Set on the container to switch to a rectangle ("rect"); anything else is freeform. */
|
|
34
|
+
export declare const LASSO_MODE_ATTR = "data-lch-lasso-mode";
|
|
35
|
+
/** The key in the shared knob bag. */
|
|
36
|
+
export declare const LASSO_KNOB_KEY = "lasso";
|
|
37
|
+
/** A chart registers { toData([x, y]), toScreen([dx, dy]) } here so the shape is stored in data coordinates.
|
|
38
|
+
* x and y are in the user space of the element the surface sits in (the plot group). */
|
|
39
|
+
export declare const LASSO_AXES_SLOT = "__lchLassoAxes";
|
|
40
|
+
/** The action list a host runs when none is configured. */
|
|
41
|
+
export declare const LASSO_DEFAULT_ACTIONS: readonly string[];
|
|
42
|
+
/** RenderOptions.lasso. `capable` is the capability the server sent with this chart (absent means not
|
|
43
|
+
* capable); `enabled` is the reader's setting (default on); both must hold. */
|
|
44
|
+
export type LassoSetting = NonNullable<RenderOptions["lasso"]>;
|
|
45
|
+
/** "on" installs a surface. The others say why not; only "over-cap" shows a caption. */
|
|
46
|
+
export type LassoVerdict = "on" | "not-capable" | "disabled" | "over-cap";
|
|
47
|
+
/** The row cap in force: the setting's when it is a positive number, else LASSO_ROW_CAP. */
|
|
48
|
+
export declare function lassoRowCap(setting: LassoSetting | null | undefined): number;
|
|
49
|
+
/** The action names to run on release, in order. Anything that is not a list of names is the default. */
|
|
50
|
+
export declare function lassoActions(setting: LassoSetting | null | undefined): string[];
|
|
51
|
+
/** Whether a chart with `rows` rows in view gets a lasso under `setting`. */
|
|
52
|
+
export declare function lassoVerdict(setting: LassoSetting | null | undefined, rows: number): LassoVerdict;
|
|
53
|
+
type Pt = [number, number];
|
|
54
|
+
export type LassoMode = "free" | "rect";
|
|
55
|
+
export type LassoSpace = "data" | "unit";
|
|
56
|
+
/** One drawn shape as stored: a freeform polygon, or the four corners of a rectangle. */
|
|
57
|
+
export interface LassoShape {
|
|
58
|
+
mode: LassoMode;
|
|
59
|
+
pts: Pt[];
|
|
60
|
+
}
|
|
61
|
+
/** What the knob bag holds under LASSO_KNOB_KEY. */
|
|
62
|
+
export interface LassoStored {
|
|
63
|
+
v: 1;
|
|
64
|
+
space: LassoSpace;
|
|
65
|
+
shapes: LassoShape[];
|
|
66
|
+
}
|
|
67
|
+
/** detail of the `llm-lasso` DOM event. `polygon` is the last shape drawn, `polygons` every shape in the
|
|
68
|
+
* lasso, both in `space` (data coordinates, or fractions of the plot box); `rows` is empty when the lasso
|
|
69
|
+
* was cleared. source: "user" a gesture or a clear, "restore" a lasso re-applied on draw, "host" a clear the
|
|
70
|
+
* host made because the selection was replaced from outside. */
|
|
71
|
+
export interface LassoEventDetail {
|
|
72
|
+
rows: number[];
|
|
73
|
+
polygon: Pt[];
|
|
74
|
+
polygons: Pt[][];
|
|
75
|
+
mode: LassoMode;
|
|
76
|
+
space: LassoSpace;
|
|
77
|
+
source: "user" | "restore" | "host";
|
|
78
|
+
}
|
|
79
|
+
export interface LassoAxes {
|
|
80
|
+
toData(p: [number, number]): ArrayLike<unknown>;
|
|
81
|
+
toScreen(p: [number, number]): ArrayLike<unknown>;
|
|
82
|
+
}
|
|
83
|
+
/** Point in a shape: the bounding box rejects first; a rectangle is then done, and a freeform polygon is
|
|
84
|
+
* an even-odd ray cast. Implemented here so the host takes no dependency on a geometry library. */
|
|
85
|
+
export declare function pointInShape(shape: {
|
|
86
|
+
mode: LassoMode;
|
|
87
|
+
pts: ReadonlyArray<{
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
}>;
|
|
91
|
+
}, x: number, y: number): boolean;
|
|
92
|
+
/** What an action is given on release. */
|
|
93
|
+
export interface LassoActionContext {
|
|
94
|
+
/** The rows inside the lasso (every shape of it), ascending. */
|
|
95
|
+
rows: number[];
|
|
96
|
+
mode: LassoMode;
|
|
97
|
+
source: "user";
|
|
98
|
+
env: LassoEnv;
|
|
99
|
+
}
|
|
100
|
+
export type LassoAction = (ctx: LassoActionContext) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Make `name` available to the action list (RenderOptions.lasso.actions). A name in the list that nothing
|
|
103
|
+
* registered is skipped, so a host can carry a list ahead of the build that understands it.
|
|
104
|
+
*/
|
|
105
|
+
export declare function registerLassoAction(name: string, action: LassoAction): void;
|
|
106
|
+
/** What the host gives the lasso. The lasso never reaches into the host's selection itself. */
|
|
107
|
+
export interface LassoEnv {
|
|
108
|
+
container: HTMLElement;
|
|
109
|
+
/** The options of the render now on screen (a function, because setOptions replaces them). */
|
|
110
|
+
options: () => RenderOptions;
|
|
111
|
+
/** How many rows the chart was handed: the number the cap is measured against. */
|
|
112
|
+
rows: number;
|
|
113
|
+
/** Publish this row set as the selection (what the "select" action does). */
|
|
114
|
+
select(rows: number[]): void;
|
|
115
|
+
/** Clear the host's selection the way an empty click does: the chart's own clear slot first when the
|
|
116
|
+
* chart owns the selection, then a published clear. A no-op when nothing is selected. */
|
|
117
|
+
clearSelection(): void;
|
|
118
|
+
}
|
|
119
|
+
export interface LassoHandle {
|
|
120
|
+
/** The surface the gesture is read on; null when the lasso is off at this row count. */
|
|
121
|
+
readonly surface: Element | null;
|
|
122
|
+
/** The rows inside the lasso now drawn, ascending; empty when none. */
|
|
123
|
+
rows(): number[];
|
|
124
|
+
/** Erase the lasso. By default it also clears the host's selection and says so with `llm-lasso`. */
|
|
125
|
+
clear(opts?: {
|
|
126
|
+
publish?: boolean;
|
|
127
|
+
source?: "user" | "host";
|
|
128
|
+
}): void;
|
|
129
|
+
/** Remove the listeners and everything the lasso drew. Nothing is persisted. */
|
|
130
|
+
destroy(): void;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Put the lasso on the chart in `env.container`, which has just drawn. Returns null when the chart gets none
|
|
134
|
+
* (not capable, switched off, nothing to draw on); a handle for the caption when the rows exceed the cap;
|
|
135
|
+
* otherwise the live handle. A lasso already installed on the container is destroyed first.
|
|
136
|
+
*/
|
|
137
|
+
export declare function installLasso(env: LassoEnv): LassoHandle | null;
|
|
138
|
+
/** Erase the lasso on `container`, if it has one, and clear the selection with it. */
|
|
139
|
+
export declare function clearLasso(container: Element | null | undefined, opts?: {
|
|
140
|
+
publish?: boolean;
|
|
141
|
+
source?: "user" | "host";
|
|
142
|
+
}): void;
|
|
143
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame fit — pure geometry.
|
|
3
|
+
*
|
|
4
|
+
* The third measured sibling of the label-contrast and legend-reconcile passes.
|
|
5
|
+
* Generated code routinely parks a footer/source caption against the
|
|
6
|
+
* RAW svg edge — `svg.append('text').attr('y', height - 2)` — instead of reserving
|
|
7
|
+
* a band for it inside `margin.bottom`. That looks right in the code and is wrong
|
|
8
|
+
* on screen: a baseline is not an ink box. A glyph descends roughly 0.27 * fontSize
|
|
9
|
+
* BELOW its baseline, so a 2px gap at font-size 7.5 puts the tails of "g", "p", "y"
|
|
10
|
+
* exactly on (or past) the frame, and `.lch-d3ctr` is `overflow:hidden` — the host
|
|
11
|
+
* silently slices them. Measured live on a Marimekko (690x340): the
|
|
12
|
+
* caption's ink box ran 331->340 in a 340-tall frame, i.e. flush, tails gone.
|
|
13
|
+
*
|
|
14
|
+
* Corpus: 41 D3 generations across ~20 chart types park text within 5 user units of
|
|
15
|
+
* the frame, so this is a RENDERER-level habit, not one chart's bug.
|
|
16
|
+
*
|
|
17
|
+
* WHY EXTEND THE VIEWBOX RATHER THAN MOVE THE TEXT: nudging a glyph back inside
|
|
18
|
+
* can shove it into whatever sits above it — in that Marimekko the axis title's ink
|
|
19
|
+
* ends 12 units above the caption, so a blind 6-unit lift would collide the two
|
|
20
|
+
* and trade a clipped line for an overlapping one. Growing the viewBox instead
|
|
21
|
+
* makes the SVG scale to fit its container: nothing moves relative to anything
|
|
22
|
+
* else, nothing is clipped, and a 2-unit grow on a 340-unit frame is a 0.6%
|
|
23
|
+
* rescale nobody can see. This is exactly the remedy the legend-reconcile pass already
|
|
24
|
+
* uses for a legend that runs off the right edge.
|
|
25
|
+
*
|
|
26
|
+
* Coordinates: EVERYTHING here is in the SVG's USER space (== viewBox units). The
|
|
27
|
+
* caller maps measured client-px boxes through `svg.getScreenCTM().inverse()`
|
|
28
|
+
* first, which captures viewBox, preserveAspectRatio, container scaling AND host
|
|
29
|
+
* page zoom in one matrix — so this module needs no px<->unit factor. Same
|
|
30
|
+
* contract as legendReconcile; the box and viewBox types are shared with it.
|
|
31
|
+
*
|
|
32
|
+
* DOM-free so it can be unit-tested on plain numbers; the DOM I/O lives in
|
|
33
|
+
* frameFitDom.fitTextToFrame.
|
|
34
|
+
*/
|
|
35
|
+
import type { Rect, ViewBox } from "./legendReconcile";
|
|
36
|
+
export type { Rect, ViewBox };
|
|
37
|
+
export interface FrameFitPlan {
|
|
38
|
+
action: "none" | "extend" | "defer";
|
|
39
|
+
top: number;
|
|
40
|
+
right: number;
|
|
41
|
+
bottom: number;
|
|
42
|
+
left: number;
|
|
43
|
+
newViewBox: ViewBox;
|
|
44
|
+
considered: number;
|
|
45
|
+
rescued: number;
|
|
46
|
+
deferred: number;
|
|
47
|
+
deferredIdx: number[];
|
|
48
|
+
reason: string;
|
|
49
|
+
}
|
|
50
|
+
export declare const FRAME_PAD = 1;
|
|
51
|
+
export declare const MAX_EXTEND_FRACTION = 0.08;
|
|
52
|
+
/**
|
|
53
|
+
* Work out how much the viewBox must grow so every measured ink box sits inside
|
|
54
|
+
* the frame with FRAME_PAD to spare.
|
|
55
|
+
*
|
|
56
|
+
* @param boxes ink boxes in USER space (see module note on the CTM mapping)
|
|
57
|
+
* @param vb the SVG's current viewBox in user units
|
|
58
|
+
* @param maxFraction per-axis extension ceiling, as a fraction of the frame
|
|
59
|
+
*/
|
|
60
|
+
export declare function planFrameFit(boxes: Rect[], vb: ViewBox, maxFraction?: number): FrameFitPlan;
|
|
61
|
+
/** Serialize a viewBox the way the SVG attribute wants it. */
|
|
62
|
+
export declare function viewBoxAttr(vb: ViewBox): string;
|
|
63
|
+
export type TextAnchor = "start" | "middle" | "end";
|
|
64
|
+
/** The minimum readable type, px. */
|
|
65
|
+
export declare const FIT_TYPE_FLOOR = 10;
|
|
66
|
+
/** A shrink never goes below this fraction of the chart's own size: past it, cut instead. */
|
|
67
|
+
export declare const FIT_SHRINK_FLOOR = 0.8;
|
|
68
|
+
/**
|
|
69
|
+
* The width, in user units, a label may keep so that its box sits inside `frame` (with
|
|
70
|
+
* FRAME_PAD), given the edge its anchor holds - or null when no shortening can fit it
|
|
71
|
+
* (it overflows vertically, or its held edge is itself outside the frame).
|
|
72
|
+
*/
|
|
73
|
+
export declare function labelRoom(box: Rect, frame: ViewBox, anchor: TextAnchor): number | null;
|
|
74
|
+
export interface LabelFitDecision {
|
|
75
|
+
action: "none" | "shrink" | "cut";
|
|
76
|
+
fontSize: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Shrink or cut: a label `width` units wide at `fontSize` px that must fit `room` units.
|
|
80
|
+
* Width scales with the font size, so the first guess is exact to within hinting; the caller
|
|
81
|
+
* re-measures after a shrink and falls through to the cut if hinting disagreed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function labelFitDecision(width: number, room: number, fontSize: number): LabelFitDecision;
|
|
84
|
+
/**
|
|
85
|
+
* The widest prefix of `full` (plus the ellipsis) that `fits`, never cut inside a number:
|
|
86
|
+
* a cut between two digits moves back to before the number ("largest 12..." of
|
|
87
|
+
* "largest 128 of 400" states a different count). Empty when nothing fits.
|
|
88
|
+
*/
|
|
89
|
+
export declare function cutToFit(full: string, fits: (s: string) => boolean, ell?: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame fit - the labels the viewBox could not rescue.
|
|
3
|
+
*
|
|
4
|
+
* fitTextToFrame grows the viewBox for text that pokes a few units past the frame and
|
|
5
|
+
* DEFERS text that is further out, because swallowing it would shrink the whole chart. A
|
|
6
|
+
* deferred label used to stay exactly where the chart put it, sliced by the frame, and
|
|
7
|
+
* a log line only counted it: a Bullet of 21 KPIs at 1230x626 reported
|
|
8
|
+
* `{"rescued":8,"deferred":6,"grew":"left+68.06"}` while six KPI names still read
|
|
9
|
+
* "...f Service Effort - Reduce effort".
|
|
10
|
+
*
|
|
11
|
+
* A label that overflows on the side its anchor does NOT hold is fitted in place: the type
|
|
12
|
+
* shrinks first (see labelFitDecision - never below the 10px floor or 80% of the chart's size),
|
|
13
|
+
* then the string is cut to the widest prefix that fits, never inside a number, and the full
|
|
14
|
+
* text rides a <title>. A label that overflows vertically, or on the side its anchor holds, is
|
|
15
|
+
* counted as unfixable and left alone. The geometry is in frameFit.ts; this is the DOM I/O,
|
|
16
|
+
* with the measurement handed in so the caller's CTM mapping (viewBox, zoom) is the only one.
|
|
17
|
+
*/
|
|
18
|
+
import { Rect, ViewBox, FrameFitPlan } from "./frameFit";
|
|
19
|
+
export interface DeferredLabelFitReport {
|
|
20
|
+
/** Deferred text elements looked at. */
|
|
21
|
+
deferred: number;
|
|
22
|
+
/** Fitted by a smaller font size alone. */
|
|
23
|
+
shrunk: number;
|
|
24
|
+
/** Fitted by cutting the string (after any shrink). */
|
|
25
|
+
cut: number;
|
|
26
|
+
/** Could not be fitted by shortening: vertical overflow, the held edge outside, rotated, tspans. */
|
|
27
|
+
unfixable: number;
|
|
28
|
+
/** The first few full labels that were fitted, for the log line. */
|
|
29
|
+
labels: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fit ONE label, `width` wide, into `room` (both in whatever units `widthNow` measures): shrink the
|
|
33
|
+
* type first (labelFitDecision - never below the 10px floor or 80% of the label's size), then cut
|
|
34
|
+
* the string to the widest prefix that fits, never inside a number, with the full text riding a
|
|
35
|
+
* <title>. `minChars` keeps at least that many characters before the ellipsis even when they do not
|
|
36
|
+
* fit - a caller that must never REMOVE a label (the nominal axis) passes 1. Shared by the
|
|
37
|
+
* frame fit and the nominal-axis cut.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fitLabelToRoom(el: Element, width: number, room: number, widthNow: () => number, minChars?: number): "none" | "shrunk" | "cut";
|
|
40
|
+
/**
|
|
41
|
+
* Fit each deferred label to `frame`. `measure` returns the element's box in the SAME user
|
|
42
|
+
* space as `frame` (null when it cannot). Never throws; a label it cannot judge is unfixable.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fitDeferredLabels(els: Element[], frame: ViewBox, measure: (el: Element) => Rect | null): DeferredLabelFitReport;
|
|
45
|
+
/** Perf ceiling: every text costs a getBoundingClientRect, and a Tabular can paint thousands of
|
|
46
|
+
* cells; above this the sweep costs more than the defect it fixes (and such charts scroll their
|
|
47
|
+
* own body anyway, so their overflow is intentional). */
|
|
48
|
+
export declare const FRAME_FIT_TEXT_CAP = 600;
|
|
49
|
+
export interface FrameFitOutcome {
|
|
50
|
+
/** Set when the pass declined: too many texts to measure. */
|
|
51
|
+
skipped?: {
|
|
52
|
+
texts: number;
|
|
53
|
+
cap: number;
|
|
54
|
+
};
|
|
55
|
+
/** The viewBox plan (planFrameFit). */
|
|
56
|
+
plan?: FrameFitPlan;
|
|
57
|
+
/** What happened to the labels the plan deferred, when there were any. */
|
|
58
|
+
labels?: DeferredLabelFitReport;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* THE WHOLE PASS, host-free: grow the viewBox for text a few units past the frame (planFrameFit),
|
|
62
|
+
* then fit the labels it deferred (fitDeferredLabels). Null when there is nothing to measure (no
|
|
63
|
+
* svg, no text, no CTM, an unreadable viewBox). A host adds its own logging around this call; it
|
|
64
|
+
* is kept DOM-only so a headless replay can run exactly what a host runs.
|
|
65
|
+
*/
|
|
66
|
+
export declare function fitTextToFrame(host: HTMLElement): FrameFitOutcome | null;
|
|
67
|
+
/** True when the element sits under a clip-path/mask, inside <defs>, or inside a scrollable
|
|
68
|
+
* subtree - all cases where being outside the frame is the chart's intent rather than a
|
|
69
|
+
* text-metric miss. */
|
|
70
|
+
export declare function isClippedOrScrolled(el: Element, stopAt: Element): boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { MIN_OVERLAP, MAX_GUTTER_FRACTION, COLORBAR_ON_PLOT_COVER, MAX_FIT_FRACTION, FIT_PAD, LEGIBILITY_GRACE_PX, MIN_LEGIBLE_PX, BOTTOM_TEXT_MIN_TICK_HITS, SWATCH_MAX_PX, SWATCH_LABEL_GAP_PX, SIGNATURE_MIN_PAIRS, AXIS_THIN_MIN_TICKS, AXIS_THIN_GAP, LABEL_ROW_MIN_TEXTS, LABEL_ROW_Y_TOLERANCE, LABEL_ROW_TRACK_COVER, LABEL_ROW_TRACK_REACH, rightmostOverlappingPlotEdge, bottommostOverlappingPlotEdge, planLegendReconcile, colorbarProbePoints, colorbarOnPlot, colorbarSitsOnMarks, colorbarReconcileSide, planContentFit, planBottomTextPush, swatchPairs, isLegendSignature, planAxisTickThin, isNumericLabelText, isDateLikeLabelText, isOrderedAxisLabelText, axisLabelsAreNominal, nominalLabelRooms, groupLabelRows, labelRowHasTrack, type Rect, type ViewBox, type ReconcileSide, type ReconcilePlan, type ReconcileOptions, type ContentFitPlan, type ContentFitOptions, type SwatchPairs, type AxisThinPlan, } from "./legendReconcile";
|
|
2
|
+
export { FRAME_PAD, MAX_EXTEND_FRACTION, FIT_TYPE_FLOOR, FIT_SHRINK_FLOOR, planFrameFit, viewBoxAttr, labelRoom, labelFitDecision, cutToFit, type FrameFitPlan, type TextAnchor, type LabelFitDecision, } from "./frameFit";
|
|
3
|
+
export { reconcileLegendInSvg, findSignatureLegendGroups, findColorbarFurniture, smallestRenderedTextPx, styleDeclaresScroll, type LegendReconcileResult, type AxisThinReport, } from "./legendReconcileDom";
|
|
4
|
+
export { FRAME_FIT_TEXT_CAP, fitTextToFrame, fitDeferredLabels, fitLabelToRoom, isClippedOrScrolled, type FrameFitOutcome, type DeferredLabelFitReport, } from "./frameFitDom";
|