@godxjp/ui 33.24.0 → 33.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/agent/START-HERE.md +5 -5
- package/agent/components/CatalogDetail.json +7 -3
- package/agent/components/DatePicker.json +1 -1
- package/agent/components/DropdownMenu.json +1 -0
- package/agent/components/EditorialCard.json +2 -1
- package/agent/components/Image.json +6 -1
- package/agent/components/Pagination.json +5 -0
- package/agent/components/Popover.json +1 -0
- package/agent/components/Select.json +2 -1
- package/agent/components/Toaster.json +1 -1
- package/agent/components.json +26 -8
- package/agent/index.json +5 -5
- package/agent/llms.txt +6 -6
- package/agent/patterns/notice-bar.json +16 -0
- package/agent/patterns-index.json +15 -0
- package/agent/patterns.json +16 -0
- package/agent/rules.json +2 -2
- package/agent/tokens.json +37 -7
- package/dist/components/charts/chart-cartesian.js +5 -1
- package/dist/components/charts/chart-summary.d.ts +1 -1
- package/dist/components/charts/chart-summary.js +17 -6
- package/dist/components/charts/compact-bar-trend.js +4 -3
- package/dist/components/charts/pie-chart.js +2 -1
- package/dist/components/data-display/image.d.ts +1 -0
- package/dist/components/data-display/image.js +9 -1
- package/dist/components/data-display/popover.js +11 -0
- package/dist/components/data-entry/calendar.js +1 -1
- package/dist/components/data-entry/date-picker.js +22 -5
- package/dist/components/general/button.js +2 -2
- package/dist/components/layout/affix.js +1 -1
- package/dist/components/layout/editorial-catalog.d.ts +1 -1
- package/dist/components/layout/editorial-catalog.js +53 -15
- package/dist/components/navigation/dropdown-menu.d.ts +13 -3
- package/dist/components/navigation/dropdown-menu.js +36 -6
- package/dist/components/navigation/pagination.d.ts +1 -1
- package/dist/components/navigation/pagination.js +13 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +1 -0
- package/dist/i18n/messages/ja.json +1 -0
- package/dist/i18n/messages/vi.json +1 -0
- package/dist/props/components/data-display.prop.d.ts +2 -0
- package/dist/props/components/layout.prop.d.ts +7 -2
- package/dist/props/components/navigation.prop.d.ts +3 -0
- package/dist/props/registry.d.ts +15 -3
- package/dist/props/registry.js +18 -1
- package/dist/styles/application.css +24 -0
- package/dist/styles/base.css +11 -0
- package/dist/styles/control.css +32 -19
- package/dist/styles/core.css +1 -0
- package/dist/styles/data-display-layout.css +7 -0
- package/dist/styles/dialog-layout.css +9 -0
- package/dist/styles/editorial-layout.css +2076 -0
- package/dist/styles/editorial.css +3 -0
- package/dist/styles/index.css +1 -0
- package/dist/styles/layers.json +39 -8
- package/dist/styles/layout.css +0 -1987
- package/dist/styles/precompiled/alert-layout.css +2 -0
- package/dist/styles/precompiled/badge-layout.css +2 -0
- package/dist/styles/precompiled/base.css +2 -0
- package/dist/styles/precompiled/card-layout.css +2 -0
- package/dist/styles/precompiled/chart-layout.css +2 -0
- package/dist/styles/precompiled/control.css +2 -0
- package/dist/styles/precompiled/data-display-layout.css +2 -0
- package/dist/styles/precompiled/data-entry-layout.css +2 -0
- package/dist/styles/precompiled/dialog-layout.css +2 -0
- package/dist/styles/precompiled/editorial-account-layout.css +2 -0
- package/dist/styles/precompiled/editorial-layout.css +2 -0
- package/dist/styles/precompiled/float-button-layout.css +2 -0
- package/dist/styles/precompiled/form-layout.css +2 -0
- package/dist/styles/precompiled/icon-layout.css +2 -0
- package/dist/styles/precompiled/layout.css +2 -0
- package/dist/styles/precompiled/logo-layout.css +2 -0
- package/dist/styles/precompiled/motion.css +2 -0
- package/dist/styles/precompiled/navigation-layout.css +2 -0
- package/dist/styles/precompiled/shell-layout.css +2 -0
- package/dist/styles/precompiled/table-layout.css +2 -0
- package/dist/styles/precompiled/text-layout.css +2 -0
- package/dist/styles/precompiled/toggle.css +2 -0
- package/dist/styles/precompiled/vendor-day-picker.css +2 -0
- package/dist/styles/precompiled/vendor-sonner.css +2 -0
- package/dist/styles.css +1 -1
- package/dist/tokens/components/control.css +3 -0
- package/dist/tokens/components/editorial.css +9 -6
- package/docs/CONSUMER-RULES.md +35 -0
- package/package.json +4 -2
- package/scripts/prune-css.mjs +56 -8
- package/scripts/ui-audit.mjs +36 -0
package/agent/tokens.json
CHANGED
|
@@ -4469,6 +4469,18 @@
|
|
|
4469
4469
|
"tier": "component",
|
|
4470
4470
|
"value": "24rem"
|
|
4471
4471
|
},
|
|
4472
|
+
{
|
|
4473
|
+
"description": "The listbox hugs its longest row up to this, then wraps rows; the same cap as the search panel so both Select modes size alike. The inset keeps it off the viewport edges.",
|
|
4474
|
+
"name": "--select-content-max-width",
|
|
4475
|
+
"tier": "component",
|
|
4476
|
+
"value": "32rem"
|
|
4477
|
+
},
|
|
4478
|
+
{
|
|
4479
|
+
"description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
|
|
4480
|
+
"name": "--select-content-viewport-inset",
|
|
4481
|
+
"tier": "component",
|
|
4482
|
+
"value": "var(--space-6)"
|
|
4483
|
+
},
|
|
4472
4484
|
{
|
|
4473
4485
|
"description": "Seat for a NUMERIC `popupMatchSelectWidth` on the plain listbox. `auto` is the inert default.",
|
|
4474
4486
|
"name": "--select-content-inline-size",
|
|
@@ -4722,7 +4734,7 @@
|
|
|
4722
4734
|
"value": "var(--font-size-sm, var(--font-size-base))"
|
|
4723
4735
|
},
|
|
4724
4736
|
{
|
|
4725
|
-
"description": "
|
|
4737
|
+
"description": "Scoped at the reader: interactive days fall back to --button-radius; read-only cells to calc(var(--radius) / var(--radius-ratio)).",
|
|
4726
4738
|
"name": "--calendar-day-radius",
|
|
4727
4739
|
"tier": "component",
|
|
4728
4740
|
"value": "initial"
|
|
@@ -9771,7 +9783,7 @@
|
|
|
9771
9783
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
9772
9784
|
"name": "--editorial-search-shadow",
|
|
9773
9785
|
"tier": "component",
|
|
9774
|
-
"value": "
|
|
9786
|
+
"value": "initial"
|
|
9775
9787
|
},
|
|
9776
9788
|
{
|
|
9777
9789
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
@@ -9891,7 +9903,7 @@
|
|
|
9891
9903
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
9892
9904
|
"name": "--editorial-search-popup-shadow",
|
|
9893
9905
|
"tier": "component",
|
|
9894
|
-
"value": "
|
|
9906
|
+
"value": "initial"
|
|
9895
9907
|
},
|
|
9896
9908
|
{
|
|
9897
9909
|
"description": "Default = hsl(var(--border)).",
|
|
@@ -10335,7 +10347,7 @@
|
|
|
10335
10347
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
10336
10348
|
"name": "--editorial-showcase-caption-shadow",
|
|
10337
10349
|
"tier": "component",
|
|
10338
|
-
"value": "
|
|
10350
|
+
"value": "initial"
|
|
10339
10351
|
},
|
|
10340
10352
|
{
|
|
10341
10353
|
"description": "scale-exempt: measured editorial geometry; retain the existing size between icon steps",
|
|
@@ -10563,7 +10575,7 @@
|
|
|
10563
10575
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
10564
10576
|
"name": "--editorial-catalog-card-hover-shadow",
|
|
10565
10577
|
"tier": "component",
|
|
10566
|
-
"value": "
|
|
10578
|
+
"value": "initial"
|
|
10567
10579
|
},
|
|
10568
10580
|
{
|
|
10569
10581
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
@@ -10989,7 +11001,7 @@
|
|
|
10989
11001
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
10990
11002
|
"name": "--editorial-spotlight-hover-shadow",
|
|
10991
11003
|
"tier": "component",
|
|
10992
|
-
"value": "
|
|
11004
|
+
"value": "initial"
|
|
10993
11005
|
},
|
|
10994
11006
|
{
|
|
10995
11007
|
"description": "── EditorialCard variant=\"journal\" ──────────────────────────────────────────────────────",
|
|
@@ -11151,7 +11163,7 @@
|
|
|
11151
11163
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
11152
11164
|
"name": "--editorial-journal-hover-shadow",
|
|
11153
11165
|
"tier": "component",
|
|
11154
|
-
"value": "
|
|
11166
|
+
"value": "initial"
|
|
11155
11167
|
},
|
|
11156
11168
|
{
|
|
11157
11169
|
"description": "── EditorialMasthead ────────────────────────────────────────────────────────────────────",
|
|
@@ -11681,6 +11693,24 @@
|
|
|
11681
11693
|
"tier": "component",
|
|
11682
11694
|
"value": "var(--space-10)"
|
|
11683
11695
|
},
|
|
11696
|
+
{
|
|
11697
|
+
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
11698
|
+
"name": "--editorial-catalog-detail-loading-background",
|
|
11699
|
+
"tier": "component",
|
|
11700
|
+
"value": "initial"
|
|
11701
|
+
},
|
|
11702
|
+
{
|
|
11703
|
+
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
11704
|
+
"name": "--editorial-catalog-detail-loading-duration",
|
|
11705
|
+
"tier": "component",
|
|
11706
|
+
"value": "var(--duration-loop)"
|
|
11707
|
+
},
|
|
11708
|
+
{
|
|
11709
|
+
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
11710
|
+
"name": "--editorial-catalog-detail-loading-alpha",
|
|
11711
|
+
"tier": "component",
|
|
11712
|
+
"value": "0.5"
|
|
11713
|
+
},
|
|
11684
11714
|
{
|
|
11685
11715
|
"description": "Editorial family tokens (gh#1435) — the public, magazine-like pages of a site built on `CenteredShell preset=\"public-landing\" flow=\"sections\"`: full-width sections, a hero with a product showcase, the ruled kicker, a section head, the inset site search, a pill navigation band, catalog / editorial cards and the brand banner. Every geometry value a design states lives here, so a theme retunes it once — including at its OWN breakpoints, by re-declaring the token under a width query in its theme file (the shapes here never hard-code a breakpoint of their own). Colour knobs are role mirrors: `initial`, with the documented default resolved at the call site, so a scoped [data-tenant] / dark override still reaches them.",
|
|
11686
11716
|
"name": "--editorial-catalog-detail-art-radius",
|
|
@@ -54,6 +54,7 @@ function CartesianChart({
|
|
|
54
54
|
assertRechartsPeer();
|
|
55
55
|
const { t, locale } = useTranslation();
|
|
56
56
|
const fmt = useChartNumberFormat(numberFormat);
|
|
57
|
+
const counts = useChartNumberFormat();
|
|
57
58
|
const list = React.useMemo(() => listFormat(locale, { style: "narrow", type: "unit" }), [locale]);
|
|
58
59
|
const points = series.flatMap(
|
|
59
60
|
(s, seriesIndex) => data.flatMap(
|
|
@@ -127,7 +128,10 @@ function CartesianChart({
|
|
|
127
128
|
categoryKey,
|
|
128
129
|
fmt,
|
|
129
130
|
list,
|
|
130
|
-
(c) => t("chart.summaryCartesian", {
|
|
131
|
+
(c) => t("chart.summaryCartesian", {
|
|
132
|
+
series: counts.format(c.series),
|
|
133
|
+
points: counts.format(c.points)
|
|
134
|
+
})
|
|
131
135
|
);
|
|
132
136
|
const resolvedHeight = chartHeight(size, height);
|
|
133
137
|
const tickFormatter = (value) => fmt.format(value);
|
|
@@ -33,7 +33,7 @@ export declare function buildTrendSummary(data: ChartDatum[], categoryKey: strin
|
|
|
33
33
|
* Negative values plot at the baseline (a compact trend has no negative axis) but
|
|
34
34
|
* still read their true figure in the text alternative.
|
|
35
35
|
*/
|
|
36
|
-
export declare function trendRatios(data: ChartDatum[], valueKey: string): number[];
|
|
36
|
+
export declare function trendRatios(data: ChartDatum[], valueKey: string): (number | null)[];
|
|
37
37
|
/**
|
|
38
38
|
* Resolve `emphasizedIndex` against a row count. Negative counts from the end (`-1` = the latest
|
|
39
39
|
* datum).
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { translateCurrent as t } from "../../i18n/translate.js";
|
|
1
2
|
function numeric(value) {
|
|
2
|
-
return typeof value === "number" && Number.isFinite(value) ? value :
|
|
3
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
4
|
+
}
|
|
5
|
+
function formatValue(value, fmt) {
|
|
6
|
+
const number = numeric(value);
|
|
7
|
+
return number === null ? t("chart.noData") : fmt.format(number);
|
|
3
8
|
}
|
|
4
9
|
function buildCartesianSummary(data, series, categoryKey, fmt, list, summaryLine) {
|
|
5
10
|
const rows = data.map((datum) => {
|
|
6
11
|
const category = String(datum[categoryKey] ?? "");
|
|
7
12
|
const parts = series.map(
|
|
8
|
-
(s) => `${s.label ?? s.dataKey}: ${
|
|
13
|
+
(s) => `${s.label ?? s.dataKey}: ${formatValue(datum[s.dataKey], fmt)}`
|
|
9
14
|
);
|
|
10
15
|
return `${category} \u2014 ${list.format(parts)}`;
|
|
11
16
|
});
|
|
@@ -13,7 +18,7 @@ function buildCartesianSummary(data, series, categoryKey, fmt, list, summaryLine
|
|
|
13
18
|
}
|
|
14
19
|
function buildSeriesRows(data, categoryKey, valueKey, fmt, annotate) {
|
|
15
20
|
return data.map((datum, index) => {
|
|
16
|
-
const row = `${String(datum[categoryKey] ?? "")}: ${
|
|
21
|
+
const row = `${String(datum[categoryKey] ?? "")}: ${formatValue(datum[valueKey], fmt)}`;
|
|
17
22
|
const note = annotate?.(index);
|
|
18
23
|
return note ? `${row} (${note})` : row;
|
|
19
24
|
});
|
|
@@ -31,9 +36,15 @@ function buildTrendSummary(data, categoryKey, valueKey, fmt, summaryLine, annota
|
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
function trendRatios(data, valueKey) {
|
|
34
|
-
const values = data.map((datum) =>
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
const values = data.map((datum) => {
|
|
40
|
+
const value = numeric(datum[valueKey]);
|
|
41
|
+
return value === null ? null : Math.max(0, value);
|
|
42
|
+
});
|
|
43
|
+
const max = values.reduce(
|
|
44
|
+
(acc, value) => value !== null && value > acc ? value : acc,
|
|
45
|
+
0
|
|
46
|
+
);
|
|
47
|
+
return values.map((value) => value === null ? null : max > 0 ? value / max : 0);
|
|
37
48
|
}
|
|
38
49
|
function resolveEmphasizedIndex(index, length) {
|
|
39
50
|
if (index === void 0 || !Number.isInteger(index) || length === 0) return -1;
|
|
@@ -25,6 +25,7 @@ function CompactBarTrend({
|
|
|
25
25
|
}) {
|
|
26
26
|
const { t } = useTranslation();
|
|
27
27
|
const fmt = useChartNumberFormat(numberFormat);
|
|
28
|
+
const countsFormat = useChartNumberFormat();
|
|
28
29
|
const hasData = data.length > 0;
|
|
29
30
|
const emphasized = resolveEmphasizedIndex(emphasizedIndex, data.length);
|
|
30
31
|
const ratios = React.useMemo(() => trendRatios(data, valueKey), [data, valueKey]);
|
|
@@ -33,7 +34,7 @@ function CompactBarTrend({
|
|
|
33
34
|
categoryKey,
|
|
34
35
|
valueKey,
|
|
35
36
|
fmt,
|
|
36
|
-
(counts) => t("chart.summaryTrend", { points:
|
|
37
|
+
(counts) => t("chart.summaryTrend", { points: countsFormat.format(counts.points) }),
|
|
37
38
|
(index) => index === emphasized ? t("chart.trendCurrent") : void 0
|
|
38
39
|
);
|
|
39
40
|
return /* @__PURE__ */ jsx(
|
|
@@ -51,14 +52,14 @@ function CompactBarTrend({
|
|
|
51
52
|
className: cn("ui-chart-trend", className),
|
|
52
53
|
id,
|
|
53
54
|
children: hasData ? /* @__PURE__ */ jsxs("div", { className: "ui-chart-trend-body", children: [
|
|
54
|
-
/* @__PURE__ */ jsx("div", { className: "ui-chart-trend-plot", "aria-hidden": "true", children: data.map((datum, index) => /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-column", children: /* @__PURE__ */ jsx(
|
|
55
|
+
/* @__PURE__ */ jsx("div", { className: "ui-chart-trend-plot", "aria-hidden": "true", children: data.map((datum, index) => /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-column", children: ratios[index] !== null ? /* @__PURE__ */ jsx(
|
|
55
56
|
"div",
|
|
56
57
|
{
|
|
57
58
|
className: "ui-chart-trend-bar",
|
|
58
59
|
"data-emphasized": index === emphasized ? "true" : void 0,
|
|
59
60
|
style: { "--chart-trend-bar-value": ratios[index] }
|
|
60
61
|
}
|
|
61
|
-
) }, `${String(datum[categoryKey])}-${index}`)) }),
|
|
62
|
+
) : null }, `${String(datum[categoryKey])}-${index}`)) }),
|
|
62
63
|
showCategoryLabels ? /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-ticks", "aria-hidden": "true", children: data.map((datum, index) => /* @__PURE__ */ jsx(
|
|
63
64
|
"span",
|
|
64
65
|
{
|
|
@@ -37,6 +37,7 @@ function PieChart({
|
|
|
37
37
|
assertRechartsPeer();
|
|
38
38
|
const { t } = useTranslation();
|
|
39
39
|
const fmt = useChartNumberFormat(numberFormat);
|
|
40
|
+
const counts = useChartNumberFormat();
|
|
40
41
|
const points = data.flatMap(
|
|
41
42
|
(datum, index) => typeof datum[dataKey] === "number" && Number.isFinite(datum[dataKey]) && Number(datum[dataKey]) > 0 ? [{ index, series: 0 }] : []
|
|
42
43
|
);
|
|
@@ -71,7 +72,7 @@ function PieChart({
|
|
|
71
72
|
dataKey,
|
|
72
73
|
nameKey,
|
|
73
74
|
fmt,
|
|
74
|
-
(c) => t("chart.summaryPie", { slices:
|
|
75
|
+
(c) => t("chart.summaryPie", { slices: counts.format(c.slices) })
|
|
75
76
|
);
|
|
76
77
|
const resolvedHeight = chartHeight(size, height);
|
|
77
78
|
return /* @__PURE__ */ jsx(
|
|
@@ -14,6 +14,7 @@ export declare const Image: React.ForwardRefExoticComponent<Omit<React.ImgHTMLAt
|
|
|
14
14
|
alt: string;
|
|
15
15
|
fallback?: string;
|
|
16
16
|
placeholder?: React.ReactNode | boolean;
|
|
17
|
+
onStatusChange?: (status: "loading" | "loaded" | "error") => void;
|
|
17
18
|
preview?: boolean | ImagePreviewConfigProp;
|
|
18
19
|
width?: number | string;
|
|
19
20
|
height?: number | string;
|
|
@@ -420,6 +420,7 @@ const ImageRoot = React.forwardRef(function Image({
|
|
|
420
420
|
alt,
|
|
421
421
|
fallback,
|
|
422
422
|
placeholder,
|
|
423
|
+
onStatusChange,
|
|
423
424
|
preview = true,
|
|
424
425
|
width,
|
|
425
426
|
height,
|
|
@@ -444,6 +445,11 @@ const ImageRoot = React.forwardRef(function Image({
|
|
|
444
445
|
status: "loading"
|
|
445
446
|
});
|
|
446
447
|
const status = load.src === src ? load.status : "loading";
|
|
448
|
+
const statusChangeRef = React.useRef(onStatusChange);
|
|
449
|
+
statusChangeRef.current = onStatusChange;
|
|
450
|
+
React.useEffect(() => {
|
|
451
|
+
statusChangeRef.current?.(status);
|
|
452
|
+
}, [src, status]);
|
|
447
453
|
const setStatus = React.useCallback(
|
|
448
454
|
(next) => setLoad(
|
|
449
455
|
(prev) => prev.src === src && prev.status === "error" ? prev : { src, status: next }
|
|
@@ -471,7 +477,9 @@ const ImageRoot = React.forwardRef(function Image({
|
|
|
471
477
|
}, [inGroup, group, id, previewSrc, alt]);
|
|
472
478
|
const [visible, setVisible] = useMergedState(config.visible, false, config.onVisibleChange);
|
|
473
479
|
const shownSrc = isError && fallback ? fallback : src;
|
|
474
|
-
const
|
|
480
|
+
const [mounted, setMounted] = React.useState(false);
|
|
481
|
+
React.useEffect(() => setMounted(true), []);
|
|
482
|
+
const showPlaceholder = mounted && placeholder != null && placeholder !== false && status === "loading";
|
|
475
483
|
const intrinsic = fit === "intrinsic";
|
|
476
484
|
const sized = intrinsic || size != null || width != null || height != null;
|
|
477
485
|
const frameFit = fit ?? (size != null || height != null ? "cover" : void 0);
|
|
@@ -243,6 +243,16 @@ function ClickContent({
|
|
|
243
243
|
}) {
|
|
244
244
|
const root = usePopoverRoot("PopoverContent");
|
|
245
245
|
const contentRef = React.useRef(null);
|
|
246
|
+
const [viewportInlineSize, setViewportInlineSize] = React.useState();
|
|
247
|
+
React.useLayoutEffect(() => {
|
|
248
|
+
if (!root.open) return;
|
|
249
|
+
const doc = root.triggerRef.current?.ownerDocument ?? document;
|
|
250
|
+
const update = () => setViewportInlineSize(doc.documentElement.clientWidth);
|
|
251
|
+
update();
|
|
252
|
+
const observer = new ResizeObserver(update);
|
|
253
|
+
observer.observe(doc.documentElement);
|
|
254
|
+
return () => observer.disconnect();
|
|
255
|
+
}, [root.open, root.triggerRef]);
|
|
246
256
|
const openAutoFocusRef = React.useRef(onOpenAutoFocus);
|
|
247
257
|
openAutoFocusRef.current = onOpenAutoFocus;
|
|
248
258
|
useOverlayCloseFocus(root.open, onCloseAutoFocus, contentRef);
|
|
@@ -350,6 +360,7 @@ function ClickContent({
|
|
|
350
360
|
style: {
|
|
351
361
|
...racStyle,
|
|
352
362
|
"--radix-popover-trigger-width": "var(--trigger-width)",
|
|
363
|
+
...viewportInlineSize == null ? null : { "--popover-viewport-inline-size": `${viewportInlineSize}px` },
|
|
353
364
|
"--radix-popover-content-transform-origin": "var(--trigger-anchor-point)",
|
|
354
365
|
...availableHeight == null ? null : {
|
|
355
366
|
"--radix-popover-content-available-height": typeof availableHeight === "number" ? `${availableHeight}px` : availableHeight
|
|
@@ -161,7 +161,7 @@ function Calendar({
|
|
|
161
161
|
day: cn("ui-calendar-day", classNames?.day),
|
|
162
162
|
day_button: cn(
|
|
163
163
|
buttonVariants({ variant: "ghost" }),
|
|
164
|
-
"ui-calendar-day-button",
|
|
164
|
+
"ui-calendar-day-button rounded-[var(--calendar-day-radius,var(--button-radius))]",
|
|
165
165
|
classNames?.day_button
|
|
166
166
|
),
|
|
167
167
|
range_start: cn("day-range-start", classNames?.range_start),
|
|
@@ -131,7 +131,7 @@ function DatePicker(props) {
|
|
|
131
131
|
defaultValue
|
|
132
132
|
);
|
|
133
133
|
const value = isControlled ? valueProp : internalValue;
|
|
134
|
-
const working = hasPending ? pending : value;
|
|
134
|
+
const working = open && hasPending ? pending : value;
|
|
135
135
|
const rangeValue = range ? working : void 0;
|
|
136
136
|
const selectedDate = range ? rangeValue?.from : Array.isArray(working) ? working[0] : working;
|
|
137
137
|
const emit = (next) => {
|
|
@@ -156,6 +156,16 @@ function DatePicker(props) {
|
|
|
156
156
|
setText(displayAll(committedSingle));
|
|
157
157
|
}
|
|
158
158
|
}, [range, value, committedFrom, committedTo, format, locale, showTime]);
|
|
159
|
+
React.useEffect(() => {
|
|
160
|
+
if (!open) {
|
|
161
|
+
setHasPending(false);
|
|
162
|
+
setPending(void 0);
|
|
163
|
+
if (range) {
|
|
164
|
+
setFromText(display(committedFrom));
|
|
165
|
+
setToText(display(committedTo));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}, [open]);
|
|
159
169
|
const valueAnchor = range ? committedRange?.from : Array.isArray(committedSingle) ? committedSingle[0] : committedSingle;
|
|
160
170
|
const [internalView, setInternalView] = React.useState(
|
|
161
171
|
() => defaultPickerValue ?? valueAnchor
|
|
@@ -182,7 +192,7 @@ function DatePicker(props) {
|
|
|
182
192
|
const validRange = (next) => Boolean(
|
|
183
193
|
next && (next.from ? allowed(next.from) : allowEmpty[0]) && (next.to ? allowed(next.to) : allowEmpty[1])
|
|
184
194
|
);
|
|
185
|
-
const choose = (input) => {
|
|
195
|
+
const choose = (input, typed = false) => {
|
|
186
196
|
if (range) {
|
|
187
197
|
let next = input;
|
|
188
198
|
if (anyDisabled && next)
|
|
@@ -196,7 +206,14 @@ function DatePicker(props) {
|
|
|
196
206
|
if (needConfirm) {
|
|
197
207
|
setPending(next);
|
|
198
208
|
setHasPending(true);
|
|
199
|
-
} else
|
|
209
|
+
} else if (open && !typed && next && !(next.from && next.to)) {
|
|
210
|
+
setPending(next);
|
|
211
|
+
setHasPending(true);
|
|
212
|
+
} else {
|
|
213
|
+
setPending(void 0);
|
|
214
|
+
setHasPending(false);
|
|
215
|
+
emit(next);
|
|
216
|
+
}
|
|
200
217
|
return;
|
|
201
218
|
}
|
|
202
219
|
let date = input;
|
|
@@ -253,7 +270,7 @@ function DatePicker(props) {
|
|
|
253
270
|
if (!trimmed && !allowEmpty[edge === "from" ? 0 : 1]) return;
|
|
254
271
|
const base = working;
|
|
255
272
|
const next = { from: base?.from, to: base?.to, [edge]: parsed && periodDate(parsed) };
|
|
256
|
-
choose(next.from || next.to ? next : void 0);
|
|
273
|
+
choose(next.from || next.to ? next : void 0, true);
|
|
257
274
|
};
|
|
258
275
|
const keyHandlers = (onEnter) => ({
|
|
259
276
|
onKeyDown: (event) => {
|
|
@@ -458,7 +475,7 @@ function DatePicker(props) {
|
|
|
458
475
|
{
|
|
459
476
|
mode: "multiple",
|
|
460
477
|
selected: Array.isArray(working) ? working : [],
|
|
461
|
-
onSelect: choose,
|
|
478
|
+
onSelect: (days) => choose(days),
|
|
462
479
|
disabled: (day) => !allowed(day),
|
|
463
480
|
...calendarShared
|
|
464
481
|
}
|
|
@@ -54,11 +54,11 @@ const buttonVariants = cva("ui-button", {
|
|
|
54
54
|
md: "ui-button--default-size",
|
|
55
55
|
// utilities after components, so only a utility can out-rank a child's own `size-*`.
|
|
56
56
|
// They read the token, so the value is still themeable.
|
|
57
|
-
xs: "ui-button--xs [&_svg:not([class*='size-'])]:size-[var(--button-xs-icon-size)]",
|
|
57
|
+
xs: "ui-button--xs [&_svg:not([class*='size-'])]:size-[var(--button-icon-size,var(--button-xs-icon-size))]",
|
|
58
58
|
sm: "ui-button--sm",
|
|
59
59
|
lg: "ui-button--lg",
|
|
60
60
|
icon: "ui-button--icon",
|
|
61
|
-
"icon-xs": "ui-button--icon-xs [&_svg]:size-[var(--button-xs-icon-size)] [&_svg]:shrink-0",
|
|
61
|
+
"icon-xs": "ui-button--icon-xs [&_svg]:size-[var(--button-icon-size,var(--button-xs-icon-size))] [&_svg]:shrink-0",
|
|
62
62
|
"icon-sm": "ui-button--icon-sm",
|
|
63
63
|
"icon-lg": "ui-button--icon-lg",
|
|
64
64
|
// The glove step (gh#1304): ≥56×56, `--control-height-touch`.
|
|
@@ -106,7 +106,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
106
106
|
};
|
|
107
107
|
}, [measure, targetElement]);
|
|
108
108
|
React.useEffect(() => {
|
|
109
|
-
if (!affixed ||
|
|
109
|
+
if (!affixed || typeof window === "undefined") return void 0;
|
|
110
110
|
let frame = null;
|
|
111
111
|
const onScroll = () => {
|
|
112
112
|
if (frame !== null) return;
|
|
@@ -7,7 +7,7 @@ export declare function EditorialFilterPanel({ variant, children, action, classN
|
|
|
7
7
|
/** One field of an `EditorialFilterPanel`: a visible label over one control. */
|
|
8
8
|
export declare function EditorialFilterField({ label, htmlFor, children, className, }: EditorialFilterFieldProp): React.JSX.Element;
|
|
9
9
|
/** The head of a catalog item's page: framed picture beside kicker, h1, tags, summary, facts. */
|
|
10
|
-
export declare function CatalogDetail({ variant, media, favorite, mediaCaption, overline, title, meta, summary, actions, facts, className, }: CatalogDetailProp): React.JSX.Element;
|
|
10
|
+
export declare function CatalogDetail({ variant, media, image, favorite, mediaCaption, overline, title, meta, summary, actions, facts, className, }: CatalogDetailProp): React.JSX.Element;
|
|
11
11
|
/** One outlined tag in a `CatalogDetail` meta row. */
|
|
12
12
|
export declare function CatalogDetailTag({ children, className }: CatalogDetailTagProp): React.JSX.Element;
|
|
13
13
|
/** One band of an item page under its head: hairline, h2, muted paragraph, then the rest. */
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
2
4
|
import { Bookmark } from "lucide-react";
|
|
5
|
+
import { Image } from "../data-display/image.js";
|
|
3
6
|
import { cn } from "../../lib/utils.js";
|
|
4
7
|
function EditorialFilterPanel({
|
|
5
8
|
variant = "panel",
|
|
@@ -37,6 +40,7 @@ function EditorialFilterField({
|
|
|
37
40
|
function CatalogDetail({
|
|
38
41
|
variant = "card",
|
|
39
42
|
media,
|
|
43
|
+
image,
|
|
40
44
|
favorite,
|
|
41
45
|
mediaCaption,
|
|
42
46
|
overline,
|
|
@@ -47,6 +51,18 @@ function CatalogDetail({
|
|
|
47
51
|
facts,
|
|
48
52
|
className
|
|
49
53
|
}) {
|
|
54
|
+
const [load, setLoad] = React.useState(null);
|
|
55
|
+
const src = image?.src;
|
|
56
|
+
const status = image ? load && load.src === src ? load.status : "loading" : void 0;
|
|
57
|
+
const onStatusChange = React.useCallback(
|
|
58
|
+
(next) => {
|
|
59
|
+
if (src !== void 0)
|
|
60
|
+
setLoad(
|
|
61
|
+
(previous) => previous?.src === src && previous.status === next ? previous : { src, status: next }
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
[src]
|
|
65
|
+
);
|
|
50
66
|
return /* @__PURE__ */ jsxs(
|
|
51
67
|
"div",
|
|
52
68
|
{
|
|
@@ -55,21 +71,43 @@ function CatalogDetail({
|
|
|
55
71
|
className: cn("ui-catalog-detail", className),
|
|
56
72
|
children: [
|
|
57
73
|
/* @__PURE__ */ jsxs("div", { className: "ui-catalog-detail-visual", children: [
|
|
58
|
-
/* @__PURE__ */ jsxs(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
/* @__PURE__ */ jsxs(
|
|
75
|
+
"div",
|
|
76
|
+
{
|
|
77
|
+
className: "ui-catalog-detail-art",
|
|
78
|
+
"data-status": status,
|
|
79
|
+
"aria-busy": status === "loading" ? true : void 0,
|
|
80
|
+
children: [
|
|
81
|
+
favorite && /* @__PURE__ */ jsx(
|
|
82
|
+
"button",
|
|
83
|
+
{
|
|
84
|
+
type: "button",
|
|
85
|
+
"aria-pressed": favorite.pressed,
|
|
86
|
+
"aria-label": favorite.label,
|
|
87
|
+
disabled: favorite.disabled,
|
|
88
|
+
className: "ui-catalog-card-favorite ui-focus-ring",
|
|
89
|
+
onClick: () => favorite.onPressedChange(!favorite.pressed),
|
|
90
|
+
children: /* @__PURE__ */ jsx(Bookmark, { "aria-hidden": true, className: "ui-catalog-card-favorite-icon" })
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
image ? /* @__PURE__ */ jsx(
|
|
94
|
+
Image,
|
|
95
|
+
{
|
|
96
|
+
src: image.src,
|
|
97
|
+
alt: image.alt,
|
|
98
|
+
width: image.width,
|
|
99
|
+
height: image.height,
|
|
100
|
+
fit: "contain",
|
|
101
|
+
fallback: image.fallback,
|
|
102
|
+
fetchPriority: image.priority ? "high" : void 0,
|
|
103
|
+
preview: false,
|
|
104
|
+
onStatusChange
|
|
105
|
+
},
|
|
106
|
+
image.src
|
|
107
|
+
) : media
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
),
|
|
73
111
|
mediaCaption != null && /* @__PURE__ */ jsx("p", { className: "ui-catalog-detail-caption", children: mediaCaption })
|
|
74
112
|
] }),
|
|
75
113
|
/* @__PURE__ */ jsxs("div", { className: "ui-catalog-detail-main", children: [
|
|
@@ -169,6 +169,14 @@ interface DropdownMenuItemPropsOwn {
|
|
|
169
169
|
description?: React.ReactNode;
|
|
170
170
|
/** Mượn thẻ của con thay vì dựng `<div>` riêng — chỗ để nhét một `<Link>` vào một mục menu. */
|
|
171
171
|
asChild?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* The item is a link to this URL. With `asChild` it is read from the borrowed child's own `href`
|
|
174
|
+
* when there is one; pass it here when the child is a router link that takes something else
|
|
175
|
+
* (TanStack/React Router `<Link to>`), so react-aria renders its LINK item — an `<a>` — instead of
|
|
176
|
+
* an action `<div>` (and stops warning "Expected <div>, got <a>"). Without a react-aria
|
|
177
|
+
* `RouterProvider` the click stays the child's own: navigation is whatever the child does.
|
|
178
|
+
*/
|
|
179
|
+
href?: string;
|
|
172
180
|
/**
|
|
173
181
|
* `id` và mọi `data-*` ĐI XUỐNG phần tử gốc của item (gh#631).
|
|
174
182
|
*
|
|
@@ -183,7 +191,7 @@ interface DropdownMenuItemPropsOwn {
|
|
|
183
191
|
id?: string;
|
|
184
192
|
}
|
|
185
193
|
type DropdownMenuItemProps = React.PropsWithChildren<DropdownMenuItemPropsOwn> & Record<`data-${string}`, unknown>;
|
|
186
|
-
export declare function DropdownMenuItem({ children, className, inset, variant, disabled, onSelect, textValue, description, asChild, ...forwarded }: DropdownMenuItemProps): React.JSX.Element;
|
|
194
|
+
export declare function DropdownMenuItem({ children, className, inset, variant, disabled, onSelect, textValue, description, asChild, href, ...forwarded }: DropdownMenuItemProps): React.JSX.Element;
|
|
187
195
|
interface DropdownMenuLabelPropsOwn {
|
|
188
196
|
className?: string;
|
|
189
197
|
inset?: boolean;
|
|
@@ -197,10 +205,12 @@ type DropdownMenuLabelProps = React.PropsWithChildren<DropdownMenuLabelPropsOwn>
|
|
|
197
205
|
* `Header` không đi qua `useRenderProps`, nên `props.children` trong `render` CHÍNH là children của
|
|
198
206
|
* consumer — không có `Provider` chen vào như ở `MenuItem`. `Slot` vì thế nhận thẳng gói props.
|
|
199
207
|
*
|
|
200
|
-
*
|
|
208
|
+
* `<header>` TRẦN thì không được: nó mang vai trò ngầm `banner`, và
|
|
201
209
|
* `banner` không nằm trong danh sách con hợp lệ của `role="menu"` (axe: aria-required-children,
|
|
202
210
|
* ARIA 1.2 — menu chỉ nhận menuitem / menuitemcheckbox / menuitemradio / group / separator). Radix
|
|
203
|
-
* dựng `<div>`, vai trò `generic`, nên nó không bao giờ vấp lỗi này.
|
|
211
|
+
* dựng `<div>`, vai trò `generic`, nên nó không bao giờ vấp lỗi này. Còn `<div>` thì RAC cảnh báo
|
|
212
|
+
* "Expected <header>, got <div>" (gh#1452). Nên: `<header role="presentation">` — đúng thẻ RAC chờ,
|
|
213
|
+
* không có landmark.
|
|
204
214
|
*
|
|
205
215
|
* `Header` vẫn được dùng chứ không thay bằng `<div>` trần: bộ dựng collection của RAC chỉ nhận các
|
|
206
216
|
* nút nó biết, một phần tử lạ đặt thẳng trong `Menu` sẽ bị nó bỏ. Nên giữ nút, đổi thẻ.
|