@unpunnyfuns/swatchbook-blocks 0.15.0 → 0.17.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/dist/index.d.mts +63 -1
- package/dist/index.mjs +569 -20
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +383 -0
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import './style.css';
|
|
2
2
|
import Color from "colorjs.io";
|
|
3
|
-
import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { addons } from "storybook/preview-api";
|
|
6
6
|
import { axes, css, cssVarPrefix, defaultTheme, diagnostics, presets, themes, themesResolved } from "virtual:swatchbook/tokens";
|
|
7
|
+
import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
|
|
7
8
|
import cx from "clsx";
|
|
8
9
|
import { analyzeAxisVariance } from "@unpunnyfuns/swatchbook-core/variance";
|
|
9
|
-
import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
|
|
10
10
|
//#region src/format-color.ts
|
|
11
11
|
const COLOR_FORMATS = [
|
|
12
12
|
"hex",
|
|
@@ -872,6 +872,533 @@ function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "as
|
|
|
872
872
|
});
|
|
873
873
|
}
|
|
874
874
|
//#endregion
|
|
875
|
+
//#region src/internal/CopyButton.tsx
|
|
876
|
+
/**
|
|
877
|
+
* Copy the given string to the clipboard and briefly surface a "Copied!"
|
|
878
|
+
* state. Falls back silently on unsupported clipboard APIs (older Safari,
|
|
879
|
+
* insecure origins) — the click still happens, the user just won't see the
|
|
880
|
+
* tick. No custom permission prompt: relies on the browser's native user-
|
|
881
|
+
* activation gate.
|
|
882
|
+
*/
|
|
883
|
+
function CopyButton$1({ value, label, variant = "icon", className }) {
|
|
884
|
+
const [copied, setCopied] = useState(false);
|
|
885
|
+
const timerRef = useRef(null);
|
|
886
|
+
useEffect(() => {
|
|
887
|
+
return () => {
|
|
888
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
889
|
+
};
|
|
890
|
+
}, []);
|
|
891
|
+
const handleClick = useCallback(() => {
|
|
892
|
+
try {
|
|
893
|
+
navigator.clipboard?.writeText(value);
|
|
894
|
+
} catch {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
setCopied(true);
|
|
898
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
899
|
+
timerRef.current = setTimeout(() => setCopied(false), 1500);
|
|
900
|
+
}, [value]);
|
|
901
|
+
const ariaLabel = label ?? `Copy ${value}`;
|
|
902
|
+
const classes = ["sb-copy-button", `sb-copy-button--${variant}`];
|
|
903
|
+
if (copied) classes.push("sb-copy-button--copied");
|
|
904
|
+
if (className) classes.push(className);
|
|
905
|
+
return /* @__PURE__ */ jsx("button", {
|
|
906
|
+
type: "button",
|
|
907
|
+
className: classes.join(" "),
|
|
908
|
+
onClick: handleClick,
|
|
909
|
+
"aria-label": ariaLabel,
|
|
910
|
+
title: ariaLabel,
|
|
911
|
+
"data-copied": copied ? "true" : void 0,
|
|
912
|
+
children: variant === "text" ? /* @__PURE__ */ jsx("span", {
|
|
913
|
+
className: "sb-copy-button__text",
|
|
914
|
+
children: copied ? "Copied" : "Copy"
|
|
915
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
916
|
+
className: "sb-copy-button__glyph",
|
|
917
|
+
"aria-hidden": true,
|
|
918
|
+
children: copied ? "✓" : "⧉"
|
|
919
|
+
})
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region src/ColorTable.tsx
|
|
924
|
+
const BASE_LABEL = "base";
|
|
925
|
+
const COLUMN_COUNT = 6;
|
|
926
|
+
function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, variants }) {
|
|
927
|
+
const { resolved, activeTheme, cssVarPrefix } = useProject();
|
|
928
|
+
const colorFormat = useColorFormat();
|
|
929
|
+
const [query, setQuery] = useState("");
|
|
930
|
+
const [selectedByBase, setSelectedByBase] = useState({});
|
|
931
|
+
const [expandedByBase, setExpandedByBase] = useState(() => /* @__PURE__ */ new Set());
|
|
932
|
+
const defs = useMemo(() => buildVariantDefs(variants), [variants]);
|
|
933
|
+
const groups = useMemo(() => {
|
|
934
|
+
const sorted = sortTokens(Object.entries(resolved).filter(([path, token]) => {
|
|
935
|
+
if (token.$type !== "color") return false;
|
|
936
|
+
return globMatch(path, filter);
|
|
937
|
+
}), {
|
|
938
|
+
by: sortBy,
|
|
939
|
+
dir: sortDir
|
|
940
|
+
});
|
|
941
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
942
|
+
for (const [path, token] of sorted) {
|
|
943
|
+
const raw = token.$value;
|
|
944
|
+
const hex = formatColor(raw, "hex");
|
|
945
|
+
const hsl = formatColor(raw, "hsl");
|
|
946
|
+
const oklch = formatColor(raw, "oklch");
|
|
947
|
+
const active = pickActiveFormat(raw, colorFormat, hex, hsl, oklch);
|
|
948
|
+
const match = matchVariant(path, defs.matchOrder);
|
|
949
|
+
const variant = {
|
|
950
|
+
label: match?.label ?? BASE_LABEL,
|
|
951
|
+
path,
|
|
952
|
+
cssVar: makeCssVar(path, cssVarPrefix),
|
|
953
|
+
value: active.value,
|
|
954
|
+
outOfGamut: active.outOfGamut,
|
|
955
|
+
hex: hex.value,
|
|
956
|
+
hsl: hsl.value,
|
|
957
|
+
oklch: oklch.value,
|
|
958
|
+
...token.$description !== void 0 && { description: token.$description },
|
|
959
|
+
...token.aliasOf !== void 0 && { aliasOf: token.aliasOf },
|
|
960
|
+
...token.aliasChain !== void 0 && { aliasChain: token.aliasChain }
|
|
961
|
+
};
|
|
962
|
+
const basePath = match?.basePath ?? path;
|
|
963
|
+
const existing = groupMap.get(basePath);
|
|
964
|
+
if (existing) existing.variants.push(variant);
|
|
965
|
+
else groupMap.set(basePath, {
|
|
966
|
+
base: basePath,
|
|
967
|
+
variants: [variant]
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
const out = [];
|
|
971
|
+
for (const { base, variants: vs } of groupMap.values()) {
|
|
972
|
+
vs.sort((a, b) => orderIndex(a.label, defs) - orderIndex(b.label, defs));
|
|
973
|
+
const searchText = vs.map((v) => `${v.path} ${v.value}`).join(" ");
|
|
974
|
+
out.push({
|
|
975
|
+
base,
|
|
976
|
+
variants: vs,
|
|
977
|
+
searchText
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
return out;
|
|
981
|
+
}, [
|
|
982
|
+
resolved,
|
|
983
|
+
filter,
|
|
984
|
+
cssVarPrefix,
|
|
985
|
+
sortBy,
|
|
986
|
+
sortDir,
|
|
987
|
+
defs,
|
|
988
|
+
colorFormat
|
|
989
|
+
]);
|
|
990
|
+
const visibleGroups = useMemo(() => {
|
|
991
|
+
if (!searchable || query.trim() === "") return groups;
|
|
992
|
+
return fuzzyFilter(groups, query, (g) => g.searchText);
|
|
993
|
+
}, [
|
|
994
|
+
groups,
|
|
995
|
+
query,
|
|
996
|
+
searchable
|
|
997
|
+
]);
|
|
998
|
+
const totalTokens = useMemo(() => groups.reduce((n, g) => n + g.variants.length, 0), [groups]);
|
|
999
|
+
const toggleExpand = useCallback((base) => {
|
|
1000
|
+
setExpandedByBase((prev) => {
|
|
1001
|
+
const next = new Set(prev);
|
|
1002
|
+
if (next.has(base)) next.delete(base);
|
|
1003
|
+
else next.add(base);
|
|
1004
|
+
return next;
|
|
1005
|
+
});
|
|
1006
|
+
}, []);
|
|
1007
|
+
const selectVariant = useCallback((base, label) => {
|
|
1008
|
+
setSelectedByBase((prev) => ({
|
|
1009
|
+
...prev,
|
|
1010
|
+
[base]: label
|
|
1011
|
+
}));
|
|
1012
|
+
}, []);
|
|
1013
|
+
const matchSuffix = searchable && query.trim() !== "" ? ` · ${visibleGroups.length} matching "${query.trim()}"` : "";
|
|
1014
|
+
const captionText = caption ?? `${totalTokens} color${totalTokens === 1 ? "" : "s"} across ${groups.length} group${groups.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""}${matchSuffix} · ${activeTheme}`;
|
|
1015
|
+
if (groups.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
1016
|
+
...themeAttrs(cssVarPrefix, activeTheme),
|
|
1017
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1018
|
+
className: "sb-block__empty",
|
|
1019
|
+
children: "No color tokens match this filter."
|
|
1020
|
+
})
|
|
1021
|
+
});
|
|
1022
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1023
|
+
...themeAttrs(cssVarPrefix, activeTheme),
|
|
1024
|
+
children: [searchable && /* @__PURE__ */ jsx("div", {
|
|
1025
|
+
className: "sb-color-table__search",
|
|
1026
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
1027
|
+
type: "search",
|
|
1028
|
+
className: "sb-color-table__search-input",
|
|
1029
|
+
placeholder: "Search colors…",
|
|
1030
|
+
value: query,
|
|
1031
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1032
|
+
"aria-label": "Fuzzy-search colors by path or value",
|
|
1033
|
+
"data-testid": "color-table-search"
|
|
1034
|
+
})
|
|
1035
|
+
}), /* @__PURE__ */ jsxs("table", {
|
|
1036
|
+
className: "sb-color-table__table",
|
|
1037
|
+
children: [
|
|
1038
|
+
/* @__PURE__ */ jsx("caption", {
|
|
1039
|
+
className: "sb-color-table__caption",
|
|
1040
|
+
children: captionText
|
|
1041
|
+
}),
|
|
1042
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1043
|
+
/* @__PURE__ */ jsx("th", {
|
|
1044
|
+
className: "sb-color-table__th sb-color-table__th--swatch",
|
|
1045
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1046
|
+
className: "sb-color-table__sr-only",
|
|
1047
|
+
children: "Swatch"
|
|
1048
|
+
})
|
|
1049
|
+
}),
|
|
1050
|
+
/* @__PURE__ */ jsx("th", {
|
|
1051
|
+
className: "sb-color-table__th sb-color-table__th--path",
|
|
1052
|
+
children: "Name"
|
|
1053
|
+
}),
|
|
1054
|
+
/* @__PURE__ */ jsx("th", {
|
|
1055
|
+
className: "sb-color-table__th",
|
|
1056
|
+
children: "Value"
|
|
1057
|
+
}),
|
|
1058
|
+
/* @__PURE__ */ jsx("th", {
|
|
1059
|
+
className: "sb-color-table__th",
|
|
1060
|
+
children: "CSS var"
|
|
1061
|
+
}),
|
|
1062
|
+
/* @__PURE__ */ jsx("th", {
|
|
1063
|
+
className: "sb-color-table__th",
|
|
1064
|
+
children: "Alias"
|
|
1065
|
+
}),
|
|
1066
|
+
/* @__PURE__ */ jsx("th", {
|
|
1067
|
+
className: "sb-color-table__th sb-color-table__th--expand",
|
|
1068
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1069
|
+
className: "sb-color-table__sr-only",
|
|
1070
|
+
children: "Expand"
|
|
1071
|
+
})
|
|
1072
|
+
})
|
|
1073
|
+
] }) }),
|
|
1074
|
+
/* @__PURE__ */ jsxs("tbody", { children: [visibleGroups.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
|
|
1075
|
+
colSpan: COLUMN_COUNT,
|
|
1076
|
+
className: "sb-color-table__td sb-color-table__empty-row",
|
|
1077
|
+
children: [
|
|
1078
|
+
"No colors match \"",
|
|
1079
|
+
query.trim(),
|
|
1080
|
+
"\"."
|
|
1081
|
+
]
|
|
1082
|
+
}) }), visibleGroups.map((group) => /* @__PURE__ */ jsx(GroupRow, {
|
|
1083
|
+
group,
|
|
1084
|
+
selectedLabel: selectedByBase[group.base],
|
|
1085
|
+
expanded: expandedByBase.has(group.base),
|
|
1086
|
+
onToggleExpand: toggleExpand,
|
|
1087
|
+
onSelectVariant: selectVariant,
|
|
1088
|
+
...onSelect !== void 0 && { onSelect }
|
|
1089
|
+
}, group.base))] })
|
|
1090
|
+
]
|
|
1091
|
+
})]
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
function GroupRow({ group, selectedLabel, expanded, onToggleExpand, onSelectVariant, onSelect }) {
|
|
1095
|
+
const multi = group.variants.length > 1;
|
|
1096
|
+
const active = group.variants.find((v) => v.label === selectedLabel) ?? group.variants[0];
|
|
1097
|
+
const nameText = multi ? group.base : active.path;
|
|
1098
|
+
const handleRowActivate = () => {
|
|
1099
|
+
if (onSelect) onSelect(active.path);
|
|
1100
|
+
else onToggleExpand(group.base);
|
|
1101
|
+
};
|
|
1102
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
|
|
1103
|
+
className: cx("sb-color-table__row", { "sb-color-table__row--expanded": expanded }),
|
|
1104
|
+
onClick: handleRowActivate,
|
|
1105
|
+
onKeyDown: (e) => {
|
|
1106
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1107
|
+
e.preventDefault();
|
|
1108
|
+
handleRowActivate();
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
tabIndex: 0,
|
|
1112
|
+
"aria-label": onSelect ? `Inspect ${active.path}` : expanded ? `Collapse ${group.base}` : `Expand ${group.base}`,
|
|
1113
|
+
"data-testid": "color-table-row",
|
|
1114
|
+
"data-path": active.path,
|
|
1115
|
+
"data-base": group.base,
|
|
1116
|
+
children: [
|
|
1117
|
+
/* @__PURE__ */ jsx("td", {
|
|
1118
|
+
className: "sb-color-table__td sb-color-table__swatch-cell",
|
|
1119
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1120
|
+
className: "sb-color-table__swatch",
|
|
1121
|
+
style: { background: active.cssVar },
|
|
1122
|
+
"aria-hidden": true
|
|
1123
|
+
})
|
|
1124
|
+
}),
|
|
1125
|
+
/* @__PURE__ */ jsxs("td", {
|
|
1126
|
+
className: cx("sb-color-table__td", "sb-color-table__path"),
|
|
1127
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1128
|
+
className: "sb-color-table__path-text",
|
|
1129
|
+
children: nameText
|
|
1130
|
+
}), multi && /* @__PURE__ */ jsx("span", {
|
|
1131
|
+
className: "sb-color-table__variant-pills",
|
|
1132
|
+
onClick: (e) => e.stopPropagation(),
|
|
1133
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
1134
|
+
role: "presentation",
|
|
1135
|
+
children: group.variants.map((v) => /* @__PURE__ */ jsx("button", {
|
|
1136
|
+
type: "button",
|
|
1137
|
+
className: cx("sb-color-table__variant-pill", { "sb-color-table__variant-pill--active": v.label === active.label }),
|
|
1138
|
+
onClick: () => onSelectVariant(group.base, v.label),
|
|
1139
|
+
"aria-pressed": v.label === active.label,
|
|
1140
|
+
"data-testid": "color-table-variant",
|
|
1141
|
+
"data-variant": v.label,
|
|
1142
|
+
children: v.label
|
|
1143
|
+
}, v.label))
|
|
1144
|
+
})]
|
|
1145
|
+
}),
|
|
1146
|
+
/* @__PURE__ */ jsx(ValueCell, {
|
|
1147
|
+
value: active.value,
|
|
1148
|
+
label: `Copy value ${active.value}`,
|
|
1149
|
+
children: active.outOfGamut && /* @__PURE__ */ jsx("span", {
|
|
1150
|
+
title: "Out of sRGB gamut for this format",
|
|
1151
|
+
"aria-label": "out of gamut",
|
|
1152
|
+
className: "sb-color-table__gamut-warn",
|
|
1153
|
+
children: "⚠"
|
|
1154
|
+
})
|
|
1155
|
+
}),
|
|
1156
|
+
/* @__PURE__ */ jsx(ValueCell, {
|
|
1157
|
+
value: active.cssVar,
|
|
1158
|
+
label: `Copy CSS var ${active.cssVar}`
|
|
1159
|
+
}),
|
|
1160
|
+
/* @__PURE__ */ jsx("td", {
|
|
1161
|
+
className: "sb-color-table__td sb-color-table__alias",
|
|
1162
|
+
children: active.aliasOf ? /* @__PURE__ */ jsx("span", {
|
|
1163
|
+
className: "sb-color-table__alias-text",
|
|
1164
|
+
children: active.aliasOf
|
|
1165
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
1166
|
+
className: "sb-color-table__alias-empty",
|
|
1167
|
+
"aria-hidden": true,
|
|
1168
|
+
children: "—"
|
|
1169
|
+
})
|
|
1170
|
+
}),
|
|
1171
|
+
/* @__PURE__ */ jsx("td", {
|
|
1172
|
+
className: "sb-color-table__td sb-color-table__expand-cell",
|
|
1173
|
+
children: !onSelect && /* @__PURE__ */ jsx("span", {
|
|
1174
|
+
className: cx("sb-color-table__chevron", { "sb-color-table__chevron--expanded": expanded }),
|
|
1175
|
+
"aria-hidden": true,
|
|
1176
|
+
children: "▸"
|
|
1177
|
+
})
|
|
1178
|
+
})
|
|
1179
|
+
]
|
|
1180
|
+
}), expanded && !onSelect && /* @__PURE__ */ jsx("tr", {
|
|
1181
|
+
className: "sb-color-table__detail-row",
|
|
1182
|
+
"data-testid": "color-table-detail",
|
|
1183
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
1184
|
+
colSpan: COLUMN_COUNT,
|
|
1185
|
+
className: "sb-color-table__td sb-color-table__detail-cell",
|
|
1186
|
+
children: /* @__PURE__ */ jsx(ExpandedDetail, {
|
|
1187
|
+
group,
|
|
1188
|
+
active
|
|
1189
|
+
})
|
|
1190
|
+
})
|
|
1191
|
+
})] });
|
|
1192
|
+
}
|
|
1193
|
+
function ExpandedDetail({ group, active }) {
|
|
1194
|
+
const hasDescription = active.description !== void 0 && active.description.length > 0;
|
|
1195
|
+
const chain = active.aliasChain && active.aliasChain.length > 0 ? active.aliasChain : void 0;
|
|
1196
|
+
const multi = group.variants.length > 1;
|
|
1197
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1198
|
+
className: "sb-color-table__detail",
|
|
1199
|
+
children: [
|
|
1200
|
+
hasDescription && /* @__PURE__ */ jsx("p", {
|
|
1201
|
+
className: "sb-color-table__description",
|
|
1202
|
+
children: active.description
|
|
1203
|
+
}),
|
|
1204
|
+
chain && /* @__PURE__ */ jsxs("p", {
|
|
1205
|
+
className: "sb-color-table__chain",
|
|
1206
|
+
children: [
|
|
1207
|
+
/* @__PURE__ */ jsx("span", {
|
|
1208
|
+
className: "sb-color-table__detail-label",
|
|
1209
|
+
children: "Alias chain:"
|
|
1210
|
+
}),
|
|
1211
|
+
" ",
|
|
1212
|
+
chain.join(" → ")
|
|
1213
|
+
]
|
|
1214
|
+
}),
|
|
1215
|
+
multi && /* @__PURE__ */ jsxs("div", {
|
|
1216
|
+
className: "sb-color-table__variant-grid",
|
|
1217
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1218
|
+
className: "sb-color-table__variant-grid-header",
|
|
1219
|
+
children: "All variants"
|
|
1220
|
+
}), /* @__PURE__ */ jsxs("table", {
|
|
1221
|
+
className: "sb-color-table__subtable",
|
|
1222
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1223
|
+
/* @__PURE__ */ jsx("th", {
|
|
1224
|
+
className: "sb-color-table__subth",
|
|
1225
|
+
children: "Variant"
|
|
1226
|
+
}),
|
|
1227
|
+
/* @__PURE__ */ jsx("th", {
|
|
1228
|
+
className: "sb-color-table__subth",
|
|
1229
|
+
children: "Path"
|
|
1230
|
+
}),
|
|
1231
|
+
/* @__PURE__ */ jsx("th", {
|
|
1232
|
+
className: "sb-color-table__subth",
|
|
1233
|
+
children: "HEX"
|
|
1234
|
+
}),
|
|
1235
|
+
/* @__PURE__ */ jsx("th", {
|
|
1236
|
+
className: "sb-color-table__subth",
|
|
1237
|
+
children: "HSL"
|
|
1238
|
+
}),
|
|
1239
|
+
/* @__PURE__ */ jsx("th", {
|
|
1240
|
+
className: "sb-color-table__subth",
|
|
1241
|
+
children: "OKLCH"
|
|
1242
|
+
})
|
|
1243
|
+
] }) }), /* @__PURE__ */ jsx("tbody", { children: group.variants.map((v) => /* @__PURE__ */ jsxs("tr", {
|
|
1244
|
+
className: cx({ "sb-color-table__subrow--active": v.label === active.label }),
|
|
1245
|
+
children: [
|
|
1246
|
+
/* @__PURE__ */ jsx("td", {
|
|
1247
|
+
className: "sb-color-table__subtd",
|
|
1248
|
+
children: v.label
|
|
1249
|
+
}),
|
|
1250
|
+
/* @__PURE__ */ jsx("td", {
|
|
1251
|
+
className: "sb-color-table__subtd sb-color-table__subtd--path",
|
|
1252
|
+
children: v.path
|
|
1253
|
+
}),
|
|
1254
|
+
/* @__PURE__ */ jsx("td", {
|
|
1255
|
+
className: "sb-color-table__subtd",
|
|
1256
|
+
children: v.hex
|
|
1257
|
+
}),
|
|
1258
|
+
/* @__PURE__ */ jsx("td", {
|
|
1259
|
+
className: "sb-color-table__subtd",
|
|
1260
|
+
children: v.hsl
|
|
1261
|
+
}),
|
|
1262
|
+
/* @__PURE__ */ jsx("td", {
|
|
1263
|
+
className: "sb-color-table__subtd",
|
|
1264
|
+
children: v.oklch
|
|
1265
|
+
})
|
|
1266
|
+
]
|
|
1267
|
+
}, v.label)) })]
|
|
1268
|
+
})]
|
|
1269
|
+
}),
|
|
1270
|
+
!hasDescription && !chain && !multi && /* @__PURE__ */ jsx("p", {
|
|
1271
|
+
className: "sb-color-table__detail-empty",
|
|
1272
|
+
children: "No description or alias chain authored for this token."
|
|
1273
|
+
})
|
|
1274
|
+
]
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
function ValueCell({ value, label, children }) {
|
|
1278
|
+
return /* @__PURE__ */ jsxs("td", {
|
|
1279
|
+
className: "sb-color-table__td sb-color-table__value-cell",
|
|
1280
|
+
children: [
|
|
1281
|
+
/* @__PURE__ */ jsx("span", {
|
|
1282
|
+
className: "sb-color-table__value-text",
|
|
1283
|
+
title: value,
|
|
1284
|
+
children: value
|
|
1285
|
+
}),
|
|
1286
|
+
children,
|
|
1287
|
+
/* @__PURE__ */ jsx("span", {
|
|
1288
|
+
className: "sb-color-table__copy-wrap",
|
|
1289
|
+
onClick: (e) => e.stopPropagation(),
|
|
1290
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
1291
|
+
role: "presentation",
|
|
1292
|
+
children: /* @__PURE__ */ jsx(CopyButton$1, {
|
|
1293
|
+
value,
|
|
1294
|
+
label,
|
|
1295
|
+
className: "sb-color-table__copy"
|
|
1296
|
+
})
|
|
1297
|
+
})
|
|
1298
|
+
]
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Pick the value + gamut flag to display in the single Value column based
|
|
1303
|
+
* on the active color-format context. We pre-compute hex/hsl/oklch for the
|
|
1304
|
+
* expanded sub-table regardless; the extras (`rgb`, `raw`) take a fresh
|
|
1305
|
+
* `formatColor` pass. Keeps the hot path fast while staying honest about
|
|
1306
|
+
* out-of-gamut warnings per-format.
|
|
1307
|
+
*/
|
|
1308
|
+
function pickActiveFormat(raw, colorFormat, hex, hsl, oklch) {
|
|
1309
|
+
switch (colorFormat) {
|
|
1310
|
+
case "hex": return {
|
|
1311
|
+
value: hex.value,
|
|
1312
|
+
outOfGamut: hex.outOfGamut
|
|
1313
|
+
};
|
|
1314
|
+
case "hsl": return {
|
|
1315
|
+
value: hsl.value,
|
|
1316
|
+
outOfGamut: hsl.outOfGamut
|
|
1317
|
+
};
|
|
1318
|
+
case "oklch": return {
|
|
1319
|
+
value: oklch.value,
|
|
1320
|
+
outOfGamut: oklch.outOfGamut
|
|
1321
|
+
};
|
|
1322
|
+
default: {
|
|
1323
|
+
const extra = formatColor(raw, colorFormat);
|
|
1324
|
+
return {
|
|
1325
|
+
value: extra.value,
|
|
1326
|
+
outOfGamut: extra.outOfGamut
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
function buildVariantDefs(variants) {
|
|
1332
|
+
if (!variants) return {
|
|
1333
|
+
matchOrder: [],
|
|
1334
|
+
displayOrder: []
|
|
1335
|
+
};
|
|
1336
|
+
const entries = [];
|
|
1337
|
+
const displayOrder = [];
|
|
1338
|
+
for (const [label, suffix] of Object.entries(variants)) {
|
|
1339
|
+
if (suffix.length === 0) continue;
|
|
1340
|
+
entries.push({
|
|
1341
|
+
label,
|
|
1342
|
+
suffix
|
|
1343
|
+
});
|
|
1344
|
+
displayOrder.push(label);
|
|
1345
|
+
}
|
|
1346
|
+
return {
|
|
1347
|
+
matchOrder: entries.toSorted((a, b) => b.suffix.length - a.suffix.length),
|
|
1348
|
+
displayOrder
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Position of a label within a group — the `base` entry always sorts first,
|
|
1353
|
+
* then declared labels in the order the caller wrote them in the `variants`
|
|
1354
|
+
* prop. Unknown labels (shouldn't happen in practice) fall to the end.
|
|
1355
|
+
*/
|
|
1356
|
+
function orderIndex(label, defs) {
|
|
1357
|
+
if (label === BASE_LABEL) return -1;
|
|
1358
|
+
const idx = defs.displayOrder.indexOf(label);
|
|
1359
|
+
return idx >= 0 ? idx : Number.POSITIVE_INFINITY;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Resolve the variant label + base path for a token, if any. The leaf
|
|
1363
|
+
* (last dot-segment) must either equal the suffix outright (dot-segment
|
|
1364
|
+
* form: `hi.disabled` matches suffix `disabled`) or end in `-<suffix>`
|
|
1365
|
+
* (hyphen-tail form: `hi-d` matches `d`). The leading hyphen is required
|
|
1366
|
+
* for the tail form so suffix `0` can't hit `neutral-900` by character.
|
|
1367
|
+
*
|
|
1368
|
+
* Returned `basePath` is what gets used as the grouping key:
|
|
1369
|
+
* - Dot-segment match → parent path (drop the last dot-segment)
|
|
1370
|
+
* - Hyphen-tail match → same dot-depth, leaf with `-<suffix>` stripped
|
|
1371
|
+
*
|
|
1372
|
+
* Entries in `matchOrder` are pre-sorted longest-first, so `h-dark` wins
|
|
1373
|
+
* over `dark` for a path ending in `-h-dark`.
|
|
1374
|
+
*/
|
|
1375
|
+
function matchVariant(path, matchOrder) {
|
|
1376
|
+
if (matchOrder.length === 0) return void 0;
|
|
1377
|
+
const segments = path.split(".");
|
|
1378
|
+
const leaf = segments.at(-1) ?? path;
|
|
1379
|
+
for (const entry of matchOrder) {
|
|
1380
|
+
if (leaf === entry.suffix) {
|
|
1381
|
+
const parent = segments.slice(0, -1).join(".");
|
|
1382
|
+
if (parent.length === 0) continue;
|
|
1383
|
+
return {
|
|
1384
|
+
label: entry.label,
|
|
1385
|
+
basePath: parent
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
const tailMarker = `-${entry.suffix}`;
|
|
1389
|
+
if (leaf.endsWith(tailMarker)) {
|
|
1390
|
+
const trimmed = leaf.slice(0, -tailMarker.length);
|
|
1391
|
+
if (trimmed.length === 0) continue;
|
|
1392
|
+
const copy = segments.slice(0, -1);
|
|
1393
|
+
copy.push(trimmed);
|
|
1394
|
+
return {
|
|
1395
|
+
label: entry.label,
|
|
1396
|
+
basePath: copy.join(".")
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
//#endregion
|
|
875
1402
|
//#region src/Diagnostics.tsx
|
|
876
1403
|
const severityLabel = {
|
|
877
1404
|
error: "ERROR",
|
|
@@ -2022,7 +2549,7 @@ function AliasChain({ path }) {
|
|
|
2022
2549
|
return [path];
|
|
2023
2550
|
}, [token, path]);
|
|
2024
2551
|
if (chain.length <= 1) return null;
|
|
2025
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
2552
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
2026
2553
|
className: "sb-token-detail__section-header",
|
|
2027
2554
|
children: "Alias chain"
|
|
2028
2555
|
}), /* @__PURE__ */ jsx("div", {
|
|
@@ -2051,7 +2578,7 @@ function AliasedBy({ path }) {
|
|
|
2051
2578
|
const tree = useMemo(() => buildAliasedByTree(path, resolved), [path, resolved]);
|
|
2052
2579
|
const truncated = useMemo(() => treeHasTruncation(tree), [tree]);
|
|
2053
2580
|
if (tree.length === 0) return null;
|
|
2054
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2581
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2055
2582
|
/* @__PURE__ */ jsx("div", {
|
|
2056
2583
|
className: "sb-token-detail__section-header",
|
|
2057
2584
|
children: "Aliased by"
|
|
@@ -2150,11 +2677,11 @@ function AxisVariance({ path }) {
|
|
|
2150
2677
|
themes,
|
|
2151
2678
|
themesResolved
|
|
2152
2679
|
]);
|
|
2153
|
-
if (themes.length === 0) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2680
|
+
if (themes.length === 0) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2154
2681
|
if (variance.kind === "constant") {
|
|
2155
2682
|
const anyTheme = themes[0];
|
|
2156
2683
|
const value = anyTheme ? formatFn(themesResolved[anyTheme.name]?.[path]) : "—";
|
|
2157
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
2684
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
2158
2685
|
className: "sb-token-detail__section-header",
|
|
2159
2686
|
children: "Values across axes"
|
|
2160
2687
|
}), /* @__PURE__ */ jsx("table", {
|
|
@@ -2190,9 +2717,9 @@ function AxisVariance({ path }) {
|
|
|
2190
2717
|
}
|
|
2191
2718
|
if (variance.kind === "one-axis") {
|
|
2192
2719
|
const axisName = variance.varyingAxes[0];
|
|
2193
|
-
if (!axisName) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2720
|
+
if (!axisName) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2194
2721
|
const axis = axes.find((a) => a.name === axisName);
|
|
2195
|
-
if (!axis) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2722
|
+
if (!axis) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2196
2723
|
const contextValues = axis.contexts.map((ctx) => {
|
|
2197
2724
|
const target = {
|
|
2198
2725
|
...activeAxes,
|
|
@@ -2208,7 +2735,7 @@ function AxisVariance({ path }) {
|
|
|
2208
2735
|
value: name ? formatFn(themesResolved[name]?.[path]) : "—"
|
|
2209
2736
|
};
|
|
2210
2737
|
});
|
|
2211
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
2738
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
2212
2739
|
className: "sb-token-detail__section-header",
|
|
2213
2740
|
children: ["Varies with ", axisName]
|
|
2214
2741
|
}), /* @__PURE__ */ jsx("table", {
|
|
@@ -2235,8 +2762,8 @@ function AxisVariance({ path }) {
|
|
|
2235
2762
|
})] });
|
|
2236
2763
|
}
|
|
2237
2764
|
const [rowAxis, colAxis, ...extra] = variance.varyingAxes.map((name) => axes.find((a) => a.name === name)).filter((a) => Boolean(a)).toSorted((a, b) => b.contexts.length - a.contexts.length);
|
|
2238
|
-
if (!rowAxis || !colAxis) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2239
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2765
|
+
if (!rowAxis || !colAxis) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2766
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2240
2767
|
/* @__PURE__ */ jsxs("div", {
|
|
2241
2768
|
className: "sb-token-detail__section-header",
|
|
2242
2769
|
children: ["Varies with ", variance.varyingAxes.join(" × ")]
|
|
@@ -2481,7 +3008,7 @@ function renderKeyValueList(rows) {
|
|
|
2481
3008
|
}
|
|
2482
3009
|
function KeyValueRow({ label, value, alias }) {
|
|
2483
3010
|
const hasAlias = alias && alias.length > 0;
|
|
2484
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
3011
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
2485
3012
|
className: "sb-token-detail__breakdown-key",
|
|
2486
3013
|
children: label
|
|
2487
3014
|
}), /* @__PURE__ */ jsxs("span", {
|
|
@@ -2737,7 +3264,7 @@ function ConsumerOutput({ path }) {
|
|
|
2737
3264
|
const { token, cssVar, activeAxes } = useTokenDetailData(path);
|
|
2738
3265
|
if (!token) return null;
|
|
2739
3266
|
const tupleLabel = Object.entries(activeAxes).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
2740
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3267
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2741
3268
|
/* @__PURE__ */ jsx("div", {
|
|
2742
3269
|
className: "sb-token-detail__section-header",
|
|
2743
3270
|
children: "Consumer output"
|
|
@@ -2817,7 +3344,7 @@ function TokenHeader({ path, heading }) {
|
|
|
2817
3344
|
"."
|
|
2818
3345
|
]
|
|
2819
3346
|
});
|
|
2820
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3347
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2821
3348
|
/* @__PURE__ */ jsx("h3", {
|
|
2822
3349
|
className: "sb-token-detail__heading",
|
|
2823
3350
|
children: heading ?? path
|
|
@@ -2840,12 +3367,19 @@ function TokenHeader({ path, heading }) {
|
|
|
2840
3367
|
function TokenUsageSnippet({ path }) {
|
|
2841
3368
|
const { token, cssVar } = useTokenDetailData(path);
|
|
2842
3369
|
if (!token) return null;
|
|
2843
|
-
|
|
3370
|
+
const snippet = `color: ${cssVar};`;
|
|
3371
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
2844
3372
|
className: "sb-token-detail__section-header",
|
|
2845
3373
|
children: "Usage"
|
|
2846
|
-
}), /* @__PURE__ */
|
|
2847
|
-
className: "sb-token-detail__snippet",
|
|
2848
|
-
children:
|
|
3374
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
3375
|
+
className: "sb-token-detail__snippet-row",
|
|
3376
|
+
children: [/* @__PURE__ */ jsx("code", {
|
|
3377
|
+
className: "sb-token-detail__snippet",
|
|
3378
|
+
children: snippet
|
|
3379
|
+
}), /* @__PURE__ */ jsx(CopyButton$1, {
|
|
3380
|
+
value: snippet,
|
|
3381
|
+
label: `Copy usage snippet ${snippet}`
|
|
3382
|
+
})]
|
|
2849
3383
|
})] });
|
|
2850
3384
|
}
|
|
2851
3385
|
//#endregion
|
|
@@ -2900,6 +3434,10 @@ function TokenDetail({ path, heading }) {
|
|
|
2900
3434
|
"aria-label": "out of gamut",
|
|
2901
3435
|
style: { marginLeft: 6 },
|
|
2902
3436
|
children: "⚠"
|
|
3437
|
+
}),
|
|
3438
|
+
/* @__PURE__ */ jsx(CopyButton$1, {
|
|
3439
|
+
value,
|
|
3440
|
+
label: `Copy value ${value}`
|
|
2903
3441
|
})
|
|
2904
3442
|
]
|
|
2905
3443
|
}),
|
|
@@ -3443,6 +3981,17 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
|
|
|
3443
3981
|
"aria-label": "out of gamut",
|
|
3444
3982
|
className: "sb-token-table__gamut-warn",
|
|
3445
3983
|
children: "⚠"
|
|
3984
|
+
}),
|
|
3985
|
+
/* @__PURE__ */ jsx("span", {
|
|
3986
|
+
className: "sb-token-table__copy-wrap",
|
|
3987
|
+
onClick: (e) => e.stopPropagation(),
|
|
3988
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
3989
|
+
role: "presentation",
|
|
3990
|
+
children: /* @__PURE__ */ jsx(CopyButton$1, {
|
|
3991
|
+
value: row.value,
|
|
3992
|
+
label: `Copy value ${row.value}`,
|
|
3993
|
+
className: "sb-token-table__copy"
|
|
3994
|
+
})
|
|
3446
3995
|
})
|
|
3447
3996
|
]
|
|
3448
3997
|
})
|
|
@@ -3550,6 +4099,6 @@ function TypographyScale({ filter, sample = "The quick brown fox jumps over the
|
|
|
3550
4099
|
});
|
|
3551
4100
|
}
|
|
3552
4101
|
//#endregion
|
|
3553
|
-
export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, CompositeBreakdown, CompositePreview, ConsumerOutput, Diagnostics, DimensionBar, DimensionScale, FontFamilySample, FontWeightScale, GradientPalette, MotionPreview, MotionSample, ShadowPreview, ShadowSample, StrokeStyleSample, SwatchbookContext, SwatchbookProvider, ThemeContext, TokenDetail, TokenHeader, TokenNavigator, TokenTable, TokenUsageSnippet, TypographyScale, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
|
|
4102
|
+
export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, ColorTable, CompositeBreakdown, CompositePreview, ConsumerOutput, Diagnostics, DimensionBar, DimensionScale, FontFamilySample, FontWeightScale, GradientPalette, MotionPreview, MotionSample, ShadowPreview, ShadowSample, StrokeStyleSample, SwatchbookContext, SwatchbookProvider, ThemeContext, TokenDetail, TokenHeader, TokenNavigator, TokenTable, TokenUsageSnippet, TypographyScale, formatColor, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, useSwatchbookData };
|
|
3554
4103
|
|
|
3555
4104
|
//# sourceMappingURL=index.mjs.map
|