@ostack.tech/ui 0.3.2 → 0.3.4

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/ostack-ui.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { forwardRef, createContext, useContext, useCallback, useRef, useEffect, useId, useMemo, useState, memo, isValidElement, cloneElement, Fragment, Children, useImperativeHandle, startTransition, useLayoutEffect as useLayoutEffect$1, createElement, useDeferredValue, useSyncExternalStore } from "react";
2
2
  import { tinykeys } from "tinykeys";
3
+ import { shallow } from "zustand/shallow";
3
4
  import fromExponential from "from-exponential";
4
5
  import { isValid, isDate, addMonths, isAfter, isBefore, startOfMonth, setMonth, getYear, setYear, startOfYear, format, isSameYear, getMonth, max, min, lastDayOfMonth, parseISO, parse, isWithinInterval, isEqual } from "date-fns";
5
6
  import { createStore, useStore, create } from "zustand";
6
7
  import { removeNumericFormat, numericFormatter, NumericFormat } from "react-number-format";
7
- import { shallow } from "zustand/shallow";
8
8
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
9
9
  import { faClose, faTriangleExclamation, faCircleExclamation, faCircleCheck, faCircleInfo, faChevronUp, faChevronDown, faRedo, faCircleQuestion, faSortDown, faSortUp, faAsterisk, faMinus, faCheck, faFilter, faArrowLeft, faArrowRight, faSearch, faChevronLeft, faChevronRight, faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
10
10
  import { ErrorBoundary as ErrorBoundary$1 } from "react-error-boundary";
@@ -410,7 +410,7 @@ function useCreateDataTableContext({
410
410
  onSelectedRowsChange,
411
411
  disabledSelections
412
412
  }) {
413
- const listeners = useLatestValues({
413
+ const listeners2 = useLatestValues({
414
414
  onOffsetChange,
415
415
  onLimitChange,
416
416
  onSort,
@@ -499,11 +499,11 @@ function useCreateDataTableContext({
499
499
  },
500
500
  setOffset: (offset2) => {
501
501
  set({ updateCounter: get().updateCounter + 1, _offset: offset2 });
502
- listeners.onOffsetChange?.(offset2);
502
+ listeners2.onOffsetChange?.(offset2);
503
503
  },
504
504
  setLimit: (limit2) => {
505
505
  set({ updateCounter: get().updateCounter + 1, limit: limit2 });
506
- listeners.onLimitChange?.(limit2);
506
+ listeners2.onLimitChange?.(limit2);
507
507
  },
508
508
  setFilter: (filter2) => {
509
509
  if (get().offset() > 0) {
@@ -511,7 +511,7 @@ function useCreateDataTableContext({
511
511
  }
512
512
  set({ filter: filter2 });
513
513
  get().actions.refresh(null);
514
- listeners.onFilterChange?.(filter2);
514
+ listeners2.onFilterChange?.(filter2);
515
515
  },
516
516
  setSort: (sortBy2, sortDirection2) => {
517
517
  if (get().offset() > 0) {
@@ -519,7 +519,7 @@ function useCreateDataTableContext({
519
519
  }
520
520
  set({ sortBy: sortBy2, sortDirection: sortDirection2 });
521
521
  get().actions.refresh();
522
- listeners.onSort?.(sortBy2, sortDirection2);
522
+ listeners2.onSort?.(sortBy2, sortDirection2);
523
523
  },
524
524
  selectRows: (keys) => {
525
525
  const {
@@ -535,7 +535,7 @@ function useCreateDataTableContext({
535
535
  }
536
536
  if (selected) {
537
537
  set({ selectedRows: [selectedRows2] });
538
- listeners.onSelectedRowsChange?.(Array.from(selectedRows2).sort());
538
+ listeners2.onSelectedRowsChange?.(Array.from(selectedRows2).sort());
539
539
  }
540
540
  },
541
541
  unselectRows: (keys) => {
@@ -551,7 +551,7 @@ function useCreateDataTableContext({
551
551
  }
552
552
  if (unselected) {
553
553
  set({ selectedRows: [selectedRows2] });
554
- listeners.onSelectedRowsChange?.(Array.from(selectedRows2).sort());
554
+ listeners2.onSelectedRowsChange?.(Array.from(selectedRows2).sort());
555
555
  }
556
556
  },
557
557
  updateWindow: (offset2, limit2) => {
@@ -10622,6 +10622,117 @@ function useKeyboardShortcut(keybinds, action, {
10622
10622
  function ignoreFormControlsKeyboardShortcut(event) {
10623
10623
  return event.target !== null && event.target instanceof HTMLElement && FORM_CONTROLS.includes(event.target.tagName);
10624
10624
  }
10625
+ function useLocation() {
10626
+ const prevLocation = useRef(currentLocation());
10627
+ const location2 = useSyncExternalStore(subscribeToHistoryEvents, () => {
10628
+ const newLocation = currentLocation();
10629
+ return shallow(prevLocation.current, newLocation) ? prevLocation.current : prevLocation.current = newLocation;
10630
+ });
10631
+ return useMemo(() => [location2, navigate], [location2]);
10632
+ }
10633
+ function useSearchParams() {
10634
+ const [location2, navigate2] = useLocation();
10635
+ const latestValues = useLatestValues({ location: location2 });
10636
+ const setSearchParams = useCallback(
10637
+ (nextSearchParams, { clearHash, ...options } = {}) => {
10638
+ const { pathname, search, hash } = latestValues.location;
10639
+ let newSearchParams = typeof nextSearchParams === "function" ? nextSearchParams(new URLSearchParams(search)) : nextSearchParams;
10640
+ if (!(newSearchParams instanceof URLSearchParams)) {
10641
+ newSearchParams = new URLSearchParams(newSearchParams);
10642
+ }
10643
+ navigate2(
10644
+ `${pathname}?${newSearchParams}${clearHash ? "" : hash}`,
10645
+ options
10646
+ );
10647
+ },
10648
+ [latestValues, navigate2]
10649
+ );
10650
+ return useMemo(
10651
+ () => [new URLSearchParams(location2.search), setSearchParams],
10652
+ [location2.search, setSearchParams]
10653
+ );
10654
+ }
10655
+ function useSearchParam(searchParam, defaultValue) {
10656
+ const [searchParams, setSearchParams] = useSearchParams();
10657
+ const searchParamValue = useMemo(
10658
+ () => searchParams.get(searchParam) ?? defaultValue,
10659
+ [defaultValue, searchParam, searchParams]
10660
+ );
10661
+ const setSearchParamValue = useCallback(
10662
+ (nextSearchParamValue, { clearDefaultValue, ...options } = {}) => {
10663
+ setSearchParams((prevSearchParams) => {
10664
+ const prevValue = prevSearchParams.get(searchParam) ?? defaultValue;
10665
+ const newValue = (typeof nextSearchParamValue === "function" ? nextSearchParamValue(prevValue) : nextSearchParamValue)?.toString();
10666
+ if (newValue == null || clearDefaultValue && newValue === defaultValue) {
10667
+ prevSearchParams.delete(searchParam);
10668
+ } else {
10669
+ prevSearchParams.set(searchParam, newValue);
10670
+ }
10671
+ return prevSearchParams;
10672
+ }, options);
10673
+ },
10674
+ [defaultValue, searchParam, setSearchParams]
10675
+ );
10676
+ return useMemo(
10677
+ () => [searchParamValue, setSearchParamValue],
10678
+ [searchParamValue, setSearchParamValue]
10679
+ );
10680
+ }
10681
+ const MONKEY_PATCHED_HISTORY = Symbol.for("ostack-ui.monkeyPatchedHistory");
10682
+ const HISTORY_EVENT_TYPES = [
10683
+ "popstate",
10684
+ "hashchange",
10685
+ // Events emitted by monkey-patched `pushState`/`replaceState` respectively
10686
+ "ostack-ui.pushstate",
10687
+ "ostack-ui.replacestate"
10688
+ ];
10689
+ const listeners = /* @__PURE__ */ new Set();
10690
+ function handleHistoryEvent(evt) {
10691
+ for (const listener of listeners) {
10692
+ listener(evt);
10693
+ }
10694
+ }
10695
+ function subscribeToHistoryEvents(listener) {
10696
+ if (!globalThis[MONKEY_PATCHED_HISTORY]) {
10697
+ if (typeof history !== "undefined") {
10698
+ for (const fnName of ["pushState", "replaceState"]) {
10699
+ const evtName = `ostack-ui.${fnName.toLowerCase()}`;
10700
+ const originalFn = history[fnName];
10701
+ history[fnName] = function() {
10702
+ const result = originalFn.apply(this, arguments);
10703
+ dispatchEvent(new Event(evtName));
10704
+ return result;
10705
+ };
10706
+ }
10707
+ }
10708
+ globalThis[MONKEY_PATCHED_HISTORY] = true;
10709
+ }
10710
+ if (listeners.size === 0) {
10711
+ for (const evtType of HISTORY_EVENT_TYPES) {
10712
+ addEventListener(evtType, handleHistoryEvent);
10713
+ }
10714
+ }
10715
+ listeners.add(listener);
10716
+ return () => {
10717
+ listeners.delete(listener);
10718
+ if (listeners.size === 0) {
10719
+ for (const evtType of HISTORY_EVENT_TYPES) {
10720
+ removeEventListener(evtType, handleHistoryEvent);
10721
+ }
10722
+ }
10723
+ };
10724
+ }
10725
+ function currentLocation() {
10726
+ const loc = window.location;
10727
+ return { pathname: loc.pathname, search: loc.search, hash: loc.hash };
10728
+ }
10729
+ function navigate(url, options) {
10730
+ history[options?.replace ? "replaceState" : "pushState"](
10731
+ options?.state,
10732
+ "",
10733
+ url
10734
+ );
10735
+ }
10625
10736
  function useIntersectionObserver(element, onIntersectionEntryChange, options) {
10626
10737
  useEffect(() => {
10627
10738
  if (element !== null) {
@@ -10853,6 +10964,7 @@ export {
10853
10964
  useLatestValues,
10854
10965
  useLayoutEffect,
10855
10966
  useLocale,
10967
+ useLocation,
10856
10968
  useMeasure,
10857
10969
  useMediaBreakpointDown,
10858
10970
  useMediaBreakpointUp,
@@ -10864,6 +10976,8 @@ export {
10864
10976
  useResponsiveValues,
10865
10977
  useScrollPosition,
10866
10978
  useScrollbarSize,
10979
+ useSearchParam,
10980
+ useSearchParams,
10867
10981
  useSetFieldControl,
10868
10982
  useSetFieldControlFocused,
10869
10983
  useSetFieldFeedback,