@unpunnyfuns/swatchbook-blocks 0.14.1 → 0.15.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 CHANGED
@@ -605,11 +605,12 @@ interface TokenNavigatorProps {
605
605
  */
606
606
  initiallyExpanded?: number;
607
607
  /**
608
- * Render a runtime search input above the tree. Matches are
609
- * case-insensitive substrings against a leaf's token path; groups
610
- * that contain no matching leaves collapse out, and every group on
611
- * the path to a match auto-expands so hits are visible without
612
- * clicking. Defaults to `true`.
608
+ * Render a runtime search input above the tree. Matches are fuzzy
609
+ * (case-insensitive, out-of-order terms, single-character typo
610
+ * tolerance) against a leaf's token path; groups that contain no
611
+ * matching leaves collapse out, and every group on the path to a
612
+ * match auto-expands so hits are visible without clicking. Defaults
613
+ * to `true`.
613
614
  */
614
615
  searchable?: boolean;
615
616
  /**
@@ -652,10 +653,11 @@ interface TokenTableProps {
652
653
  sortDir?: SortDir;
653
654
  /**
654
655
  * Render a runtime search input above the table that narrows rows by
655
- * case-insensitive substring against the token path, type, or value.
656
- * Defaults to `true` because browsing a multi-hundred-token reference
657
- * without search is painful. Pass `false` to hide the input (the
658
- * `filter` / `type` props still apply at authoring time).
656
+ * fuzzy match (case-insensitive, out-of-order terms, single-character
657
+ * typo tolerance) against the token path, type, or value. Defaults to
658
+ * `true` because browsing a multi-hundred-token reference without
659
+ * search is painful. Pass `false` to hide the input (the `filter` /
660
+ * `type` props still apply at authoring time).
659
661
  */
660
662
  searchable?: boolean;
661
663
  /**
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ import { addons } from "storybook/preview-api";
6
6
  import { axes, css, cssVarPrefix, defaultTheme, diagnostics, presets, themes, themesResolved } from "virtual:swatchbook/tokens";
7
7
  import cx from "clsx";
8
8
  import { analyzeAxisVariance } from "@unpunnyfuns/swatchbook-core/variance";
9
+ import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
9
10
  //#region src/format-color.ts
10
11
  const COLOR_FORMATS = [
11
12
  "hex",
@@ -3015,18 +3016,21 @@ function countLeaves(node) {
3015
3016
  for (const c of node.children) n += countLeaves(c);
3016
3017
  return n;
3017
3018
  }
3019
+ function collectLeafPaths(nodes, out) {
3020
+ for (const node of nodes) if (node.kind === "leaf") out.push(node.path);
3021
+ else collectLeafPaths(node.children, out);
3022
+ }
3018
3023
  /**
3019
- * Return a pruned copy of the tree containing only leaves whose path
3020
- * matches `needle` (case-insensitive substring) plus the groups on the
3021
- * way to them. Every surviving group's path is added to `expandOut` so
3022
- * callers can force those groups open.
3024
+ * Return a pruned copy of the tree keeping only leaves whose path is in
3025
+ * `matches`, plus the groups on the way to them. Every surviving group's
3026
+ * path is added to `expandOut` so callers can force those groups open.
3023
3027
  */
3024
- function pruneTreeForSearch(nodes, needle, expandOut) {
3028
+ function pruneTreeForMatches(nodes, matches, expandOut) {
3025
3029
  const out = [];
3026
3030
  for (const node of nodes) if (node.kind === "leaf") {
3027
- if (node.path.toLowerCase().includes(needle)) out.push(node);
3031
+ if (matches.has(node.path)) out.push(node);
3028
3032
  } else {
3029
- const children = pruneTreeForSearch(node.children, needle, expandOut);
3033
+ const children = pruneTreeForMatches(node.children, matches, expandOut);
3030
3034
  if (children.length > 0) {
3031
3035
  expandOut.add(node.path);
3032
3036
  out.push({
@@ -3064,10 +3068,12 @@ function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true,
3064
3068
  visibleTree: tree,
3065
3069
  searchExpanded: null
3066
3070
  };
3067
- const needle = query.trim().toLowerCase();
3071
+ const leafPaths = [];
3072
+ collectLeafPaths(tree, leafPaths);
3073
+ const matches = new Set(fuzzyFilter(leafPaths, query, (p) => p));
3068
3074
  const expandOut = /* @__PURE__ */ new Set();
3069
3075
  return {
3070
- visibleTree: pruneTreeForSearch(tree, needle, expandOut),
3076
+ visibleTree: matches.size === 0 ? [] : pruneTreeForMatches(tree, matches, expandOut),
3071
3077
  searchExpanded: expandOut
3072
3078
  };
3073
3079
  }, [
@@ -3340,8 +3346,7 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
3340
3346
  ]);
3341
3347
  const visibleRows = useMemo(() => {
3342
3348
  if (!searchable || query.trim() === "") return rows;
3343
- const needle = query.trim().toLowerCase();
3344
- return rows.filter((row) => row.path.toLowerCase().includes(needle) || row.type.toLowerCase().includes(needle) || row.value.toLowerCase().includes(needle));
3349
+ return fuzzyFilter(rows, query, (row) => `${row.path} ${row.type} ${row.value}`);
3345
3350
  }, [
3346
3351
  rows,
3347
3352
  query,
@@ -3371,7 +3376,7 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
3371
3376
  placeholder: "Search tokens…",
3372
3377
  value: query,
3373
3378
  onChange: (e) => setQuery(e.target.value),
3374
- "aria-label": "Search tokens by path, type, or value",
3379
+ "aria-label": "Fuzzy-search tokens by path, type, or value",
3375
3380
  "data-testid": "token-table-search"
3376
3381
  })
3377
3382
  }),