@ox0/hooks 0.2.2 → 0.3.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.ts CHANGED
@@ -11,6 +11,7 @@ export * from "./use-hover";
11
11
  export * from "./use-interval";
12
12
  export * from "./use-key-down";
13
13
  export * from "./use-stored-state";
14
+ export * from "./use-search-history";
14
15
  export * from "./use-long-press";
15
16
  export * from "./use-press";
16
17
  export * from "./use-media-query";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -502,6 +502,49 @@ function useSessionState(key, parser, options) {
502
502
  function useSessionSharedState(key, parser, options) {
503
503
  return useStoredState("session", key, parser, options, true);
504
504
  }
505
+ const entriesParser = {
506
+ parse (value) {
507
+ if (!Array.isArray(value)) throw new Error("search history is not a list");
508
+ return value.filter((entry)=>"string" == typeof entry);
509
+ }
510
+ };
511
+ function searchHistoryIdentity(entry) {
512
+ return entry.trim().replace(/\s+/g, " ").toLowerCase();
513
+ }
514
+ function useSearchHistory(key, options = {}) {
515
+ const { limit = 15, storage = "persistent" } = options;
516
+ const storageKey = `search-history:${key}`;
517
+ const persistent = usePersistentSharedState(storageKey, entriesParser, {
518
+ defaultValue: []
519
+ });
520
+ const session = useSessionSharedState(storageKey, entriesParser, {
521
+ defaultValue: []
522
+ });
523
+ const [entries, setEntries] = "session" === storage ? session : persistent;
524
+ return useMemo(()=>({
525
+ entries,
526
+ add (entry) {
527
+ const text = entry.trim();
528
+ if ("" === text) return;
529
+ const identity = searchHistoryIdentity(text);
530
+ setEntries((previous)=>[
531
+ text,
532
+ ...previous.filter((existing)=>searchHistoryIdentity(existing) !== identity)
533
+ ].slice(0, limit));
534
+ },
535
+ remove (entry) {
536
+ const identity = searchHistoryIdentity(entry);
537
+ setEntries((previous)=>previous.filter((existing)=>searchHistoryIdentity(existing) !== identity));
538
+ },
539
+ clear () {
540
+ setEntries([]);
541
+ }
542
+ }), [
543
+ entries,
544
+ limit,
545
+ setEntries
546
+ ]);
547
+ }
505
548
  function useLongPress({ delayMs = 500, moveThresholdPx = 8, disabled = false, onLongPress }) {
506
549
  const timerRef = useRef(null);
507
550
  const pointerIdRef = useRef(null);
@@ -717,4 +760,4 @@ function useWindowSize() {
717
760
  return size;
718
761
  }
719
762
  export { isFocusVisible, usePress } from "@react-aria/interactions";
720
- export { StorageProvider, useBeforeUnloadWarning, useBreakpoint, useContainerSize, useControllableState, useCopyToClipboard, useDebouncedValue, useDeferredPresence, useDisclosure, useEventListener, useHover, useInterval, useKeyDown, useLongPress, useMediaQuery, useNavigatorOnline, useNow, useOS, usePersistentSharedState, usePersistentState, usePointerCoarse, usePrefersReducedMotion, usePrevious, useSessionSharedState, useSessionState, useTimeout, useViewport, useWindowSize };
763
+ export { StorageProvider, useBeforeUnloadWarning, useBreakpoint, useContainerSize, useControllableState, useCopyToClipboard, useDebouncedValue, useDeferredPresence, useDisclosure, useEventListener, useHover, useInterval, useKeyDown, useLongPress, useMediaQuery, useNavigatorOnline, useNow, useOS, usePersistentSharedState, usePersistentState, usePointerCoarse, usePrefersReducedMotion, usePrevious, useSearchHistory, useSessionSharedState, useSessionState, useTimeout, useViewport, useWindowSize };
@@ -0,0 +1,24 @@
1
+ /** Recent searches, newest first. Pass it to a search box (`QueryBar`, `GlobalSearch`) as `history`. */
2
+ export type SearchHistory = {
3
+ readonly entries: readonly string[];
4
+ /** Records a search: moves it (or an earlier copy of it) to the top, and trims to the limit. */
5
+ readonly add: (entry: string) => void;
6
+ readonly remove: (entry: string) => void;
7
+ readonly clear: () => void;
8
+ };
9
+ export type SearchHistoryOptions = {
10
+ /** How many entries to keep. Default 15. */
11
+ readonly limit?: number;
12
+ /**
13
+ * `"persistent"` (default): kept across sessions in localStorage. `"session"`: forgotten when the
14
+ * tab closes — consider it when searches can contain personal data.
15
+ */
16
+ readonly storage?: "persistent" | "session";
17
+ };
18
+ /**
19
+ * Recent searches for one search box, kept per user through `StorageProvider` and shared between
20
+ * tabs. Entries are deduplicated ignoring case and spacing; blank searches aren't recorded. Use a
21
+ * key per box (`"payments"`, `"global-search"`) so their histories stay apart.
22
+ */
23
+ export declare function useSearchHistory(key: string, options?: SearchHistoryOptions): SearchHistory;
24
+ //# sourceMappingURL=use-search-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-search-history.d.ts","sourceRoot":"","sources":["../src/use-search-history.ts"],"names":[],"mappings":"AAIA,wGAAwG;AACxG,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,gGAAgG;IAChG,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CAC5C,CAAA;AAcD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAiC/F"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-search-history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-search-history.test.d.ts","sourceRoot":"","sources":["../src/use-search-history.test.tsx"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ox0/hooks",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/esturcke/ox0-platform.git",
@@ -27,7 +27,7 @@
27
27
  "react": "^19.0.0"
28
28
  },
29
29
  "scripts": {
30
- "build": "rslib build -c ../../rslib.config.ts && tsc --build --force tsconfig.build.json",
30
+ "build": "rslib build -c ../../rslib.config.ts && rm -f tsconfig.build.tsbuildinfo && tsc -p tsconfig.build.json",
31
31
  "typecheck": "tsc -p tsconfig.json",
32
32
  "lint": "oxlint .",
33
33
  "test": "vitest run"