@unpunnyfuns/swatchbook-blocks 0.69.1 → 1.0.0-alpha.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.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import './style.css';
2
2
  import { COLOR_FORMATS } from "@unpunnyfuns/swatchbook-core/color-formats";
3
3
  import { formatColor, parseColor } from "@unpunnyfuns/swatchbook-core/format-color";
4
- import { createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { getVariance, listPaths, resolveAllWithProvenanceAt } from "@unpunnyfuns/swatchbook-core/graph";
6
6
  import { makeCssVar } from "@unpunnyfuns/swatchbook-core/css-var";
7
7
  import { SWATCHBOOK_STYLE_ELEMENT_ID, ensureStyleElement } from "@unpunnyfuns/swatchbook-core/style-element";
@@ -11,6 +11,7 @@ import { dataAttr } from "@unpunnyfuns/swatchbook-core/data-attr";
11
11
  import { matchPath } from "@unpunnyfuns/swatchbook-core/match-path";
12
12
  import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
13
13
  import cx from "clsx";
14
+ import { useWindowVirtualizer } from "@tanstack/react-virtual";
14
15
  //#region src/internal/channel.ts
15
16
  let channel = null;
16
17
  const pending = /* @__PURE__ */ new Set();
@@ -4969,12 +4970,120 @@ function TokenTableView({ rows, activeTheme, cssVarPrefix, activeAxes, colorForm
4969
4970
  deferredQuery,
4970
4971
  searchable
4971
4972
  ]);
4973
+ const VIRTUALIZE_THRESHOLD = 50;
4974
+ const scrollParentRef = useRef(null);
4975
+ const [scrollMargin, setScrollMargin] = useState(0);
4976
+ const virtualize = typeof window !== "undefined" && visibleRows.length >= VIRTUALIZE_THRESHOLD;
4977
+ useLayoutEffect(() => {
4978
+ if (!virtualize) return;
4979
+ const el = scrollParentRef.current;
4980
+ if (!el) return;
4981
+ const update = () => setScrollMargin(el.getBoundingClientRect().top + window.scrollY);
4982
+ update();
4983
+ const observer = new ResizeObserver(update);
4984
+ observer.observe(document.body);
4985
+ window.addEventListener("resize", update);
4986
+ return () => {
4987
+ observer.disconnect();
4988
+ window.removeEventListener("resize", update);
4989
+ };
4990
+ }, [virtualize]);
4991
+ const virtualizer = useWindowVirtualizer({
4992
+ count: virtualize ? visibleRows.length : 0,
4993
+ estimateSize: () => 40,
4994
+ overscan: 8,
4995
+ scrollMargin
4996
+ });
4997
+ const virtualItems = virtualize ? virtualizer.getVirtualItems() : [];
4998
+ const padTop = virtualItems.length > 0 ? (virtualItems[0]?.start ?? 0) - scrollMargin : 0;
4999
+ const padBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - ((virtualItems[virtualItems.length - 1]?.end ?? 0) - scrollMargin) : 0;
4972
5000
  const handleRowClick = useCallback((path) => {
4973
5001
  if (onSelect) onSelect(path);
4974
5002
  else setSelectedPath(path);
4975
5003
  }, [onSelect, setSelectedPath]);
4976
5004
  const matchSuffix = searchable && query.trim() !== "" ? ` · ${visibleRows.length} matching "${query.trim()}"` : "";
4977
5005
  const captionText = caption ?? `${rows.length} token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""}${type ? ` · $type=${type}` : ""}${matchSuffix} · ${activeTheme}`;
5006
+ const renderRow = (row, rowIndex, measureRef) => {
5007
+ const token = row.token;
5008
+ return /* @__PURE__ */ jsxs("tr", {
5009
+ ref: measureRef,
5010
+ "data-index": rowIndex,
5011
+ "aria-rowindex": rowIndex + 2,
5012
+ className: "sb-token-table__row",
5013
+ onClick: () => handleRowClick(row.path),
5014
+ onKeyDown: (e) => {
5015
+ if (e.key === "Enter" || e.key === " ") {
5016
+ e.preventDefault();
5017
+ handleRowClick(row.path);
5018
+ }
5019
+ },
5020
+ tabIndex: 0,
5021
+ "aria-haspopup": "dialog",
5022
+ "aria-label": `Inspect ${row.path}`,
5023
+ "data-testid": "token-table-row",
5024
+ "data-path": row.path,
5025
+ children: [
5026
+ /* @__PURE__ */ jsx("td", {
5027
+ className: cx("sb-token-table__td", "sb-token-table__path"),
5028
+ "data-deprecated": enabledIndicators.deprecation && row.isDeprecated ? "true" : void 0,
5029
+ children: row.path
5030
+ }),
5031
+ /* @__PURE__ */ jsx("td", {
5032
+ className: "sb-token-table__td",
5033
+ children: /* @__PURE__ */ jsxs("span", {
5034
+ className: "sb-token-table__value-cell",
5035
+ children: [
5036
+ row.type && /* @__PURE__ */ jsx("span", {
5037
+ className: "sb-token-table__type-pill",
5038
+ children: row.type
5039
+ }),
5040
+ row.isColor && /* @__PURE__ */ jsx("span", {
5041
+ className: "sb-token-table__swatch",
5042
+ style: { background: row.cssVar },
5043
+ "aria-hidden": true
5044
+ }),
5045
+ /* @__PURE__ */ jsx("span", {
5046
+ className: "sb-token-table__value-text",
5047
+ title: row.value,
5048
+ "data-testid": "token-table-value",
5049
+ children: row.value
5050
+ }),
5051
+ row.outOfGamut && /* @__PURE__ */ jsx("span", {
5052
+ title: "Out of sRGB gamut for this format",
5053
+ "aria-label": "out of gamut",
5054
+ className: "sb-token-table__gamut-warn",
5055
+ children: "⚠"
5056
+ }),
5057
+ /* @__PURE__ */ jsx("span", {
5058
+ className: "sb-token-table__copy-wrap",
5059
+ onClick: (e) => e.stopPropagation(),
5060
+ onKeyDown: (e) => e.stopPropagation(),
5061
+ role: "presentation",
5062
+ children: /* @__PURE__ */ jsx(CopyButton$1, {
5063
+ value: row.value,
5064
+ label: `Copy value ${row.value}`,
5065
+ className: "sb-token-table__copy"
5066
+ })
5067
+ })
5068
+ ]
5069
+ })
5070
+ }),
5071
+ /* @__PURE__ */ jsx("td", {
5072
+ className: "sb-token-table__td sb-token-table__refs",
5073
+ children: token && /* @__PURE__ */ jsx(RowIndicators, {
5074
+ path: row.path,
5075
+ token,
5076
+ root: void 0,
5077
+ variance: row.variance,
5078
+ colorFormat,
5079
+ canReference: (p) => validPaths.has(p),
5080
+ onReferenceClick: (p) => setSelectedPath(p),
5081
+ enabled: enabledIndicators
5082
+ })
5083
+ })
5084
+ ]
5085
+ }, row.path);
5086
+ };
4978
5087
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
4979
5088
  ...blockWrapperAttrs(cssVarPrefix, activeAxes),
4980
5089
  children: /* @__PURE__ */ jsx("div", {
@@ -5005,114 +5114,62 @@ function TokenTableView({ rows, activeTheme, cssVarPrefix, activeAxes, colorForm
5005
5114
  }),
5006
5115
  /* @__PURE__ */ jsxs("table", {
5007
5116
  className: "sb-token-table__table",
5117
+ "aria-rowcount": visibleRows.length + 1,
5008
5118
  children: [
5009
5119
  /* @__PURE__ */ jsx("caption", {
5010
5120
  className: "sb-token-table__caption",
5011
5121
  children: captionText
5012
5122
  }),
5013
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
5014
- /* @__PURE__ */ jsx("th", {
5015
- className: cx("sb-token-table__th", "sb-token-table__th--path"),
5016
- children: "Path"
5017
- }),
5018
- /* @__PURE__ */ jsx("th", {
5019
- className: cx("sb-token-table__th", "sb-token-table__th--value"),
5020
- children: "Value"
5021
- }),
5022
- /* @__PURE__ */ jsx("th", {
5023
- className: "sb-token-table__th sb-token-table__th--refs",
5024
- children: /* @__PURE__ */ jsx("span", {
5025
- className: "sb-token-table__sr-status",
5026
- children: "References and status"
5123
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
5124
+ "aria-rowindex": 1,
5125
+ children: [
5126
+ /* @__PURE__ */ jsx("th", {
5127
+ className: cx("sb-token-table__th", "sb-token-table__th--path"),
5128
+ children: "Path"
5129
+ }),
5130
+ /* @__PURE__ */ jsx("th", {
5131
+ className: cx("sb-token-table__th", "sb-token-table__th--value"),
5132
+ children: "Value"
5133
+ }),
5134
+ /* @__PURE__ */ jsx("th", {
5135
+ className: "sb-token-table__th sb-token-table__th--refs",
5136
+ children: /* @__PURE__ */ jsx("span", {
5137
+ className: "sb-token-table__sr-status",
5138
+ children: "References and status"
5139
+ })
5027
5140
  })
5028
- })
5029
- ] }) }),
5030
- /* @__PURE__ */ jsxs("tbody", { children: [visibleRows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
5031
- colSpan: 3,
5032
- className: "sb-token-table__td sb-token-table__empty-row",
5141
+ ]
5142
+ }) }),
5143
+ /* @__PURE__ */ jsxs("tbody", {
5144
+ ref: scrollParentRef,
5033
5145
  children: [
5034
- "No tokens match \"",
5035
- query.trim(),
5036
- "\"."
5146
+ visibleRows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
5147
+ colSpan: 3,
5148
+ className: "sb-token-table__td sb-token-table__empty-row",
5149
+ children: [
5150
+ "No tokens match \"",
5151
+ query.trim(),
5152
+ "\"."
5153
+ ]
5154
+ }) }),
5155
+ !virtualize && visibleRows.map((row, i) => renderRow(row, i)),
5156
+ virtualize && padTop > 0 && /* @__PURE__ */ jsx("tr", {
5157
+ "aria-hidden": "true",
5158
+ style: { height: padTop },
5159
+ children: /* @__PURE__ */ jsx("td", { colSpan: 3 })
5160
+ }),
5161
+ virtualize && virtualItems.map((vi) => {
5162
+ const row = visibleRows[vi.index];
5163
+ if (!row) return null;
5164
+ return renderRow(row, vi.index, virtualizer.measureElement);
5165
+ }),
5166
+ virtualize && padBottom > 0 && /* @__PURE__ */ jsx("tr", {
5167
+ "aria-hidden": "true",
5168
+ style: { height: padBottom },
5169
+ children: /* @__PURE__ */ jsx("td", { colSpan: 3 })
5170
+ })
5037
5171
  ]
5038
- }) }), visibleRows.map((row) => {
5039
- const token = row.token;
5040
- return /* @__PURE__ */ jsxs("tr", {
5041
- className: "sb-token-table__row",
5042
- onClick: () => handleRowClick(row.path),
5043
- onKeyDown: (e) => {
5044
- if (e.key === "Enter" || e.key === " ") {
5045
- e.preventDefault();
5046
- handleRowClick(row.path);
5047
- }
5048
- },
5049
- tabIndex: 0,
5050
- "aria-haspopup": "dialog",
5051
- "aria-label": `Inspect ${row.path}`,
5052
- "data-testid": "token-table-row",
5053
- "data-path": row.path,
5054
- children: [
5055
- /* @__PURE__ */ jsx("td", {
5056
- className: cx("sb-token-table__td", "sb-token-table__path"),
5057
- "data-deprecated": enabledIndicators.deprecation && row.isDeprecated ? "true" : void 0,
5058
- children: row.path
5059
- }),
5060
- /* @__PURE__ */ jsx("td", {
5061
- className: "sb-token-table__td",
5062
- children: /* @__PURE__ */ jsxs("span", {
5063
- className: "sb-token-table__value-cell",
5064
- children: [
5065
- row.type && /* @__PURE__ */ jsx("span", {
5066
- className: "sb-token-table__type-pill",
5067
- children: row.type
5068
- }),
5069
- row.isColor && /* @__PURE__ */ jsx("span", {
5070
- className: "sb-token-table__swatch",
5071
- style: { background: row.cssVar },
5072
- "aria-hidden": true
5073
- }),
5074
- /* @__PURE__ */ jsx("span", {
5075
- className: "sb-token-table__value-text",
5076
- title: row.value,
5077
- "data-testid": "token-table-value",
5078
- children: row.value
5079
- }),
5080
- row.outOfGamut && /* @__PURE__ */ jsx("span", {
5081
- title: "Out of sRGB gamut for this format",
5082
- "aria-label": "out of gamut",
5083
- className: "sb-token-table__gamut-warn",
5084
- children: "⚠"
5085
- }),
5086
- /* @__PURE__ */ jsx("span", {
5087
- className: "sb-token-table__copy-wrap",
5088
- onClick: (e) => e.stopPropagation(),
5089
- onKeyDown: (e) => e.stopPropagation(),
5090
- role: "presentation",
5091
- children: /* @__PURE__ */ jsx(CopyButton$1, {
5092
- value: row.value,
5093
- label: `Copy value ${row.value}`,
5094
- className: "sb-token-table__copy"
5095
- })
5096
- })
5097
- ]
5098
- })
5099
- }),
5100
- /* @__PURE__ */ jsx("td", {
5101
- className: "sb-token-table__td sb-token-table__refs",
5102
- children: token && /* @__PURE__ */ jsx(RowIndicators, {
5103
- path: row.path,
5104
- token,
5105
- root: void 0,
5106
- variance: row.variance,
5107
- colorFormat,
5108
- canReference: (p) => validPaths.has(p),
5109
- onReferenceClick: (p) => setSelectedPath(p),
5110
- enabled: enabledIndicators
5111
- })
5112
- })
5113
- ]
5114
- }, row.path);
5115
- })] })
5172
+ })
5116
5173
  ]
5117
5174
  }),
5118
5175
  selectedPath !== null && /* @__PURE__ */ jsx(DetailOverlay, {