@ggts-sh/svelte 0.43.0 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -96,54 +96,9 @@ export type PlotRootStyleInput = {
96
96
  * Empty concatenation → `undefined` (matches host `… || undefined`).
97
97
  */
98
98
  export declare function plotRootInlineStyle(input: PlotRootStyleInput): string | undefined;
99
- /** Edge/gap pad between a pressed legend box and the Clear recovery control. */
100
- export declare const CLEAR_CONTROL_GAP_PX = 4;
101
- /**
102
- * Compact control height — matches discrete legend row targets (min 24px AA
103
- * target size) rather than the old 44×44 AAA square that dwarfed swatches.
104
- */
105
- export declare const CLEAR_CONTROL_HEIGHT_PX = 24;
99
+ export { CLEAR_CONTROL_GAP_PX, CLEAR_CONTROL_HEIGHT_PX, resolveClearControlLayout, type ClearControlLayout, } from "@ggts-sh/core/interaction";
106
100
  /** Grace period after pointer leaves legend chrome before Clear fades for screenshots. */
107
101
  export declare const CLEAR_HIDE_DELAY_MS = 2500;
108
- export type ClearControlLayoutInput = {
109
- /**
110
- * Host: `interactionConfig.legendFocus !== null`.
111
- * Must stay even when `pressedScale` is non-null: controller emphasis can
112
- * briefly leave a pressed scale after legend focus is disabled; Clear is
113
- * intentionally suppressed then.
114
- */
115
- readonly legendFocusEnabled: boolean;
116
- /** Host: `effectiveLegendPressed?.scale ?? null`. */
117
- readonly pressedScale: string | null;
118
- /** Scene legends — full box + position so Clear anchors off the pressed guide. */
119
- readonly legends: readonly {
120
- readonly scale: string;
121
- readonly x: number;
122
- readonly y: number;
123
- readonly width: number;
124
- readonly height: number;
125
- readonly position?: "right" | "bottom";
126
- readonly direction?: "vertical" | "horizontal";
127
- }[];
128
- readonly sceneWidth: number;
129
- readonly sceneHeight: number;
130
- };
131
- export type ClearControlLayout = {
132
- readonly left: number;
133
- readonly top: number;
134
- };
135
- /**
136
- * Scene-local position for the legend Clear recovery control, or null to hide.
137
- *
138
- * Placement (collision-safe relative to the pressed legend):
139
- * - **right / vertical** — prefer **below** the stack; if that would clamp into
140
- * the legend box (tall low guide), try **above**, then **left** of the box
141
- * - **bottom / horizontal** — prefer **right** of the strip; else under, then above
142
- *
143
- * Candidates are clamped into the scene, then the first that does not intersect
144
- * the pressed legend box wins. null when focus is off / nothing pressed / no match.
145
- */
146
- export declare function resolveClearControlLayout(input: ClearControlLayoutInput): ClearControlLayout | null;
147
102
  /**
148
103
  * Whether the Clear control should paint (opacity / pointer-events) while a
149
104
  * series remains pressed. Host owns the hide timer; this pure gate only
@@ -91,76 +91,9 @@ export function plotRootInlineStyle(input) {
91
91
  : "";
92
92
  return `${sizeCss}${input.themeStyle}` || undefined;
93
93
  }
94
- /** Edge/gap pad between a pressed legend box and the Clear recovery control. */
95
- export const CLEAR_CONTROL_GAP_PX = 4;
96
- /**
97
- * Compact control height — matches discrete legend row targets (min 24px AA
98
- * target size) rather than the old 44×44 AAA square that dwarfed swatches.
99
- */
100
- export const CLEAR_CONTROL_HEIGHT_PX = 24;
101
- /** Approximate painted width of the "Clear" label + horizontal padding (layout clamp only). */
102
- const CLEAR_CONTROL_WIDTH_PX = 44;
94
+ export { CLEAR_CONTROL_GAP_PX, CLEAR_CONTROL_HEIGHT_PX, resolveClearControlLayout, } from "@ggts-sh/core/interaction";
103
95
  /** Grace period after pointer leaves legend chrome before Clear fades for screenshots. */
104
96
  export const CLEAR_HIDE_DELAY_MS = 2500;
105
- /**
106
- * Scene-local position for the legend Clear recovery control, or null to hide.
107
- *
108
- * Placement (collision-safe relative to the pressed legend):
109
- * - **right / vertical** — prefer **below** the stack; if that would clamp into
110
- * the legend box (tall low guide), try **above**, then **left** of the box
111
- * - **bottom / horizontal** — prefer **right** of the strip; else under, then above
112
- *
113
- * Candidates are clamped into the scene, then the first that does not intersect
114
- * the pressed legend box wins. null when focus is off / nothing pressed / no match.
115
- */
116
- export function resolveClearControlLayout(input) {
117
- if (!input.legendFocusEnabled || input.pressedScale === null)
118
- return null;
119
- const legend = input.legends.find((entry) => entry.scale === input.pressedScale);
120
- if (legend === undefined)
121
- return null;
122
- const position = legend.position ?? "right";
123
- const direction = legend.direction ?? (position === "bottom" ? "horizontal" : "vertical");
124
- const gap = CLEAR_CONTROL_GAP_PX;
125
- const btnW = CLEAR_CONTROL_WIDTH_PX;
126
- const btnH = CLEAR_CONTROL_HEIGHT_PX;
127
- const maxLeft = Math.max(gap, input.sceneWidth - btnW - gap);
128
- const maxTop = Math.max(gap, input.sceneHeight - btnH - gap);
129
- const clamp = (left, top) => ({
130
- left: Math.min(Math.max(gap, left), maxLeft),
131
- top: Math.min(Math.max(gap, top), maxTop),
132
- });
133
- const overlapsLegend = (layout) => {
134
- const right = layout.left + btnW;
135
- const bottom = layout.top + btnH;
136
- const legRight = legend.x + legend.width;
137
- const legBottom = legend.y + legend.height;
138
- return !(right <= legend.x ||
139
- layout.left >= legRight ||
140
- bottom <= legend.y ||
141
- layout.top >= legBottom);
142
- };
143
- const candidates = position === "bottom" || direction === "horizontal"
144
- ? [
145
- // beside, then under, then above the horizontal strip
146
- clamp(legend.x + legend.width + gap, legend.y + Math.max(0, (legend.height - btnH) / 2)),
147
- clamp(legend.x, legend.y + legend.height + gap),
148
- clamp(legend.x, legend.y - gap - btnH),
149
- ]
150
- : [
151
- // under, then above, then left of the vertical stack
152
- clamp(legend.x, legend.y + legend.height + gap),
153
- clamp(legend.x, legend.y - gap - btnH),
154
- clamp(legend.x - gap - btnW, legend.y),
155
- ];
156
- for (const candidate of candidates) {
157
- if (!overlapsLegend(candidate))
158
- return candidate;
159
- }
160
- // Last resort: first candidate (still scene-clamped) when every park collides
161
- // (degenerate: legend fills the scene). Prefer under/beside intent over hide.
162
- return candidates[0] ?? null;
163
- }
164
97
  /**
165
98
  * Whether the Clear control should paint (opacity / pointer-events) while a
166
99
  * series remains pressed. Host owns the hide timer; this pure gate only
@@ -1,16 +1 @@
1
- /** Interaction theme roles exposed as CSS custom properties on the plot root.
2
- * Values may be color strings or numeric opacities (e.g. interactionMuted). */
3
- export type PlotThemeCssTokens = {
4
- readonly interactionInk: string | number;
5
- readonly interactionMuted: string | number;
6
- readonly focusRing: string | number;
7
- readonly crosshair: string | number;
8
- readonly selectionFill: string | number;
9
- readonly selectionStroke: string | number;
10
- readonly tooltipPaper: string | number;
11
- readonly tooltipInk: string | number;
12
- readonly tooltipBorder: string | number;
13
- readonly toolActive: string | number;
14
- };
15
- /** Stable role-order CSS custom property string for interaction chrome. */
16
- export declare function themeTokensToCss(tokens: PlotThemeCssTokens): string;
1
+ export { themeTokensToCss } from "@ggts-sh/core/interaction";
@@ -1,16 +1 @@
1
- const THEME_ROLES = [
2
- "interactionInk",
3
- "interactionMuted",
4
- "focusRing",
5
- "crosshair",
6
- "selectionFill",
7
- "selectionStroke",
8
- "tooltipPaper",
9
- "tooltipInk",
10
- "tooltipBorder",
11
- "toolActive",
12
- ];
13
- /** Stable role-order CSS custom property string for interaction chrome. */
14
- export function themeTokensToCss(tokens) {
15
- return THEME_ROLES.map((role) => `--gg-theme-${role}:${tokens[role]}`).join(";");
16
- }
1
+ export { themeTokensToCss } from "@ggts-sh/core/interaction";
@@ -1,10 +1 @@
1
- import type { ResolvedInspectMode } from "../interaction/interaction.js";
2
- export type InspectGuideAxes = {
3
- readonly vertical: boolean;
4
- readonly horizontal: boolean;
5
- };
6
- /**
7
- * Which screen-space crosshairs an inspect mode paints.
8
- * `coord_flip` swaps x/y guides; exact paints none; xy paints both.
9
- */
10
- export declare function inspectGuideAxes(mode: ResolvedInspectMode, flipped: boolean): InspectGuideAxes;
1
+ export { inspectGuideAxes } from "@ggts-sh/core/interaction";
@@ -1,24 +1 @@
1
- /**
2
- * Which screen-space crosshairs an inspect mode paints.
3
- * `coord_flip` swaps x/y guides; exact paints none; xy paints both.
4
- */
5
- export function inspectGuideAxes(mode, flipped) {
6
- switch (mode) {
7
- case "exact":
8
- return { vertical: false, horizontal: false };
9
- case "xy":
10
- return { vertical: true, horizontal: true };
11
- case "x":
12
- return flipped
13
- ? { vertical: false, horizontal: true }
14
- : { vertical: true, horizontal: false };
15
- case "y":
16
- return flipped
17
- ? { vertical: true, horizontal: false }
18
- : { vertical: false, horizontal: true };
19
- default: {
20
- const unexpected = mode;
21
- throw new TypeError(`unexpected inspect mode ${String(unexpected)}`);
22
- }
23
- }
24
- }
1
+ export { inspectGuideAxes } from "@ggts-sh/core/interaction";
@@ -6,14 +6,4 @@
6
6
  * belongs with that chrome — flat (gridless) themes drop to text on paper and
7
7
  * stay silent; pinned-disabled inspect configs never advertise pinning.
8
8
  */
9
- export type TooltipPinHintInput = {
10
- /** Whether inspect pinning is enabled on the plot. */
11
- readonly pin: boolean;
12
- /**
13
- * Resolved theme tooltip keyline. Gridless themes derive `"transparent"`;
14
- * a concrete color means the theme still wants a boxed panel.
15
- */
16
- readonly tooltipBorder: string;
17
- };
18
- /** True when the default tooltip should show pin-related instructional footers. */
19
- export declare function shouldShowTooltipPinHint(input: TooltipPinHintInput): boolean;
9
+ export { shouldShowTooltipPinHint } from "@ggts-sh/core/interaction";
@@ -6,12 +6,4 @@
6
6
  * belongs with that chrome — flat (gridless) themes drop to text on paper and
7
7
  * stay silent; pinned-disabled inspect configs never advertise pinning.
8
8
  */
9
- /** True when the default tooltip should show pin-related instructional footers. */
10
- export function shouldShowTooltipPinHint(input) {
11
- if (!input.pin)
12
- return false;
13
- const border = input.tooltipBorder.trim().toLowerCase();
14
- if (border === "transparent" || border === "none")
15
- return false;
16
- return true;
17
- }
9
+ export { shouldShowTooltipPinHint } from "@ggts-sh/core/interaction";
@@ -34,29 +34,13 @@ export type ChooseToolAction = {
34
34
  } | {
35
35
  readonly type: "apply";
36
36
  };
37
- export type ShowToolRailInput = {
38
- readonly interactive: boolean;
39
- readonly availableToolCount: number;
40
- /** Host: `interactionConfig.select?.type === "point"`. */
41
- readonly canPublishPointSelection: boolean;
42
- readonly selectedKeyCount: number;
43
- readonly hasIntervalSelection: boolean;
44
- readonly hasZoomDomains: boolean;
45
- };
46
- /**
47
- * True when every batch has zero rows (including an empty batches list).
48
- * Host still requires a live model before calling this.
49
- */
37
+ export { shouldShowToolRail } from "@ggts-sh/core/interaction";
38
+ /** True when every batch has zero rows (including an empty batches list). */
50
39
  export declare function isEmptyPlotScene(batches: readonly {
51
40
  readonly rowIndex: {
52
41
  readonly length: number;
53
42
  };
54
43
  }[]): boolean;
55
- /**
56
- * Whether the tool rail chrome should mount.
57
- * Multi-tool mode, or any recovery control (point clear / interval clear / zoom reset).
58
- */
59
- export declare function shouldShowToolRail(input: ShowToolRailInput): boolean;
60
44
  export type InertSelectionOverlayInput = {
61
45
  /**
62
46
  * Host chart-local `interactive` (tools or legend focus).
@@ -33,24 +33,11 @@ export function resolveEffectiveTool(requested, available) {
33
33
  return requested;
34
34
  return available[0] ?? "inspect";
35
35
  }
36
- /**
37
- * True when every batch has zero rows (including an empty batches list).
38
- * Host still requires a live model before calling this.
39
- */
36
+ export { shouldShowToolRail } from "@ggts-sh/core/interaction";
37
+ /** True when every batch has zero rows (including an empty batches list). */
40
38
  export function isEmptyPlotScene(batches) {
41
39
  return batches.every((batch) => batch.rowIndex.length === 0);
42
40
  }
43
- /**
44
- * Whether the tool rail chrome should mount.
45
- * Multi-tool mode, or any recovery control (point clear / interval clear / zoom reset).
46
- */
47
- export function shouldShowToolRail(input) {
48
- return (input.interactive &&
49
- (input.availableToolCount > 1 ||
50
- (input.canPublishPointSelection && input.selectedKeyCount > 0) ||
51
- input.hasIntervalSelection ||
52
- input.hasZoomDomains));
53
- }
54
41
  /**
55
42
  * Whether to paint the non-interactive selection/emphasis overlay.
56
43
  * Host also must not be in surface-interactive mode; SceneOverlays
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggts-sh/svelte",
3
- "version": "0.43.0",
3
+ "version": "0.43.1",
4
4
  "description": "ggts: a layered grammar of graphics for Svelte 5 — ggplot2 semantics, JSON-serializable specs, agent-friendly validation, and hybrid SVG/canvas rendering. The ggts render CLI ships separately as @ggts-sh/cli.",
5
5
  "keywords": [
6
6
  "charts",
@@ -56,9 +56,9 @@
56
56
  "test:coverage": "vitest run --coverage --project chromium && vitest run --coverage --config vitest.ssr.config.ts"
57
57
  },
58
58
  "dependencies": {
59
- "@ggts-sh/compose": "^0.43.0",
60
- "@ggts-sh/core": "^0.43.0",
61
- "@ggts-sh/spec": "^0.43.0"
59
+ "@ggts-sh/compose": "^0.43.1",
60
+ "@ggts-sh/core": "^0.43.1",
61
+ "@ggts-sh/spec": "^0.43.1"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@sveltejs/package": "^2.5.8",
@@ -68,7 +68,7 @@
68
68
  "@vitest/coverage-v8": "^4.1.11",
69
69
  "axe-core": "^4.13.0",
70
70
  "playwright": "1.61.1",
71
- "svelte": "^5.56.10",
71
+ "svelte": "^5.57.0",
72
72
  "svelte-check": "^4.7.4",
73
73
  "typescript": "^6.0.3",
74
74
  "vite": "^8.2.0",