fix(studio): render complete interactive gallery previews
This commit is contained in:
1 parent
c765d76a06
commit
9520651330
26 files changed
+5761
-39
No files matched your search
@@ -73,7 +73,7 @@ component DeviceFrame {
|
||||
border: 0.65rem solid var(--wrn-color-text);
|
||||
border-radius: 2rem;
|
||||
background: var(--wrn-color-background);
|
||||
transition: width var(--wrn-motion-normal) var(--wrn-ease-standard);
|
||||
transition: width var(--wrn-motion-base) var(--wrn-ease-standard);
|
||||
}
|
||||
|
||||
.wrn-next--device-frame[data-device="tablet"] .wrn-next__device-shell {
|
||||
|
||||
@@ -52,7 +52,8 @@ component Switch {
|
||||
height: 1.375rem;
|
||||
margin: 0;
|
||||
border: 1px solid color-mix(in srgb, var(--wrn-color-border-strong, var(--wrn-color-border)) 70%, transparent);
|
||||
border-radius: 999px;
|
||||
/* A switch track keeps its semantic pill geometry, including older themes. */
|
||||
border-radius: 999px !important;
|
||||
background: var(--wrn-color-surface-muted, var(--wrn-color-surface-2));
|
||||
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.118",
|
||||
"version": "0.8.120",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import type { ChartConfiguration, ChartData, ChartType } from "chart.js";
|
||||
|
||||
/** Shorten only the painted tick. Dataset labels remain intact for tooltips and tables. */
|
||||
export function chartTickLabel(label: unknown, limit: number): string {
|
||||
const text = String(label ?? "");
|
||||
if (text.length <= limit) return text;
|
||||
const start = Math.ceil((limit - 1) / 2);
|
||||
return text.slice(0, start) + "…" + text.slice(-(limit - 1 - start));
|
||||
}
|
||||
export function chartTooltipLines(label: unknown, limit: number): string[] {
|
||||
const lines: string[] = [];
|
||||
for (const part of String(label ?? "").split("\n")) {
|
||||
if (!part.length) lines.push("");
|
||||
for (let index = 0; index < part.length; index += limit)
|
||||
lines.push(part.slice(index, index + limit));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
export interface ChartProps {
|
||||
title?: string;
|
||||
items?: unknown[];
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import Chart from "chart.js/auto";
|
||||
import type { ChartConfiguration, Plugin } from "chart.js";
|
||||
import { chartConfiguration, mergeOptions, type ChartProps } from "./chart-config";
|
||||
import {
|
||||
chartConfiguration,
|
||||
mergeOptions,
|
||||
chartTickLabel,
|
||||
chartTooltipLines,
|
||||
type ChartProps,
|
||||
} from "./chart-config";
|
||||
|
||||
const selector = '[data-wrnexus-runtime~="chartjs"][data-chart-config]';
|
||||
const mounted = new Map<
|
||||
@@ -95,6 +101,36 @@ function configuration(root: HTMLElement, props: ChartProps): ChartConfiguration
|
||||
},
|
||||
config.options,
|
||||
);
|
||||
if (["bar", "line"].includes(config.type)) {
|
||||
const axis = config.options!.indexAxis === "y" ? "y" : "x";
|
||||
const interaction = config.options!.interaction;
|
||||
if (interaction?.mode === "index" && !interaction.axis) interaction.axis = axis;
|
||||
const scale = ((config.options!.scales ??= {})[axis] ??= {});
|
||||
if (!scale.type || scale.type === "category") {
|
||||
const ticks = (scale.ticks ??= {});
|
||||
if (!ticks.callback)
|
||||
ticks.callback = function (value) {
|
||||
return chartTickLabel(
|
||||
this.getLabelForValue(Number(value)),
|
||||
this.chart.width < 480 ? 18 : 36,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
const tooltip = config.options!.plugins && config.options!.plugins.tooltip;
|
||||
if ((tooltip as unknown) !== false && (config.options!.plugins as unknown) !== false) {
|
||||
const plugin = (config.options!.plugins ??= {});
|
||||
const options = (plugin.tooltip ??= {});
|
||||
const callbacks = (options.callbacks ??= {});
|
||||
if (!callbacks.title)
|
||||
callbacks.title = (items) =>
|
||||
items.length
|
||||
? chartTooltipLines(
|
||||
items[0].label,
|
||||
Math.max(12, Math.min(80, Math.floor((items[0].chart.width - 32) / 8))),
|
||||
)
|
||||
: [];
|
||||
}
|
||||
config.options!.onClick = (event, elements, chart) => {
|
||||
if (!elements.length) return;
|
||||
const { index, datasetIndex } = elements[0];
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { chartConfiguration, chartTypes } from "../src/chart-config";
|
||||
import {
|
||||
chartConfiguration,
|
||||
chartTypes,
|
||||
chartTickLabel,
|
||||
chartTooltipLines,
|
||||
} from "../src/chart-config";
|
||||
import { renderComponent, mountHtml } from "../../test/src/index";
|
||||
import { discoverPlugins, createPluginRunner } from "../../plugin/src/index";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const source = readFileSync(new URL("../components/Chart.wrn", import.meta.url), "utf8");
|
||||
test("compact category ticks preserve the complete tooltip text", () => {
|
||||
const label = "2026-10-17T10:03:00 · completed · audit-four";
|
||||
expect(chartTickLabel(label, 18)).toHaveLength(18);
|
||||
expect(chartTickLabel(label, 18)).toContain("…");
|
||||
expect(chartTickLabel("Short", 18)).toBe("Short");
|
||||
const lines = chartTooltipLines(label, 18);
|
||||
expect(lines.every((line) => line.length <= 18)).toBe(true);
|
||||
expect(lines.join("")).toBe(label);
|
||||
});
|
||||
for (const type of chartTypes) {
|
||||
test(`Chart.js ${type} preserves native datasets`, () => {
|
||||
const data = {
|
||||
|
||||
Reference in new issue
Block a user