@ostack.tech/ui 0.3.1 → 0.3.3

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) => {
@@ -4977,6 +4977,8 @@ const PagedDataTablePagination = forwardRef(function PagedDataTablePagination2({
4977
4977
  rowsRange,
4978
4978
  previousPageButtonLabel,
4979
4979
  nextPageButtonLabel,
4980
+ previousPageButtonProps,
4981
+ nextPageButtonProps,
4980
4982
  className,
4981
4983
  ...otherProps
4982
4984
  }, forwardedRef) {
@@ -5026,19 +5028,29 @@ const PagedDataTablePagination = forwardRef(function PagedDataTablePagination2({
5026
5028
  /* @__PURE__ */ jsx(
5027
5029
  IconButton,
5028
5030
  {
5031
+ variant: "subtle",
5029
5032
  icon: faArrowLeft,
5030
5033
  label: previousPageButtonLabel ?? "",
5031
- disabled: currentPage === 0,
5032
- onClick: () => setOffsetWithTransition((currentPage - 1) * limit)
5034
+ ...previousPageButtonProps,
5035
+ disabled: currentPage === 0 || previousPageButtonProps?.disabled,
5036
+ onClick: combineEventHandlers(
5037
+ (() => setOffsetWithTransition((currentPage - 1) * limit)),
5038
+ previousPageButtonProps?.onClick
5039
+ )
5033
5040
  }
5034
5041
  ),
5035
5042
  /* @__PURE__ */ jsx(
5036
5043
  IconButton,
5037
5044
  {
5045
+ variant: "subtle",
5038
5046
  icon: faArrowRight,
5039
5047
  label: nextPageButtonLabel ?? "",
5040
- disabled: currentPage === lastPage,
5041
- onClick: () => setOffsetWithTransition((currentPage + 1) * limit)
5048
+ ...nextPageButtonProps,
5049
+ disabled: currentPage === lastPage || nextPageButtonProps?.disabled,
5050
+ onClick: combineEventHandlers(
5051
+ (() => setOffsetWithTransition((currentPage + 1) * limit)),
5052
+ nextPageButtonProps?.onClick
5053
+ )
5042
5054
  }
5043
5055
  )
5044
5056
  ] })
@@ -5074,7 +5086,14 @@ const ScrolledDataTablePagination = forwardRef(function ScrolledDataTablePaginat
5074
5086
  }
5075
5087
  );
5076
5088
  });
5077
- const DataTablePagination = forwardRef(function DataTablePagination2({ rowsRange, previousPageButtonLabel, nextPageButtonLabel, ...otherProps }, forwardedRef) {
5089
+ const DataTablePagination = forwardRef(function DataTablePagination2({
5090
+ rowsRange,
5091
+ previousPageButtonLabel,
5092
+ nextPageButtonLabel,
5093
+ previousPageButtonProps,
5094
+ nextPageButtonProps,
5095
+ ...otherProps
5096
+ }, forwardedRef) {
5078
5097
  const [locale] = useLocale();
5079
5098
  rowsRange ??= locale.DataTablePagination.rowsRange;
5080
5099
  previousPageButtonLabel ??= locale.DataTablePagination.previousPageButtonLabel;
@@ -5087,6 +5106,8 @@ const DataTablePagination = forwardRef(function DataTablePagination2({ rowsRange
5087
5106
  rowsRange,
5088
5107
  previousPageButtonLabel,
5089
5108
  nextPageButtonLabel,
5109
+ previousPageButtonProps,
5110
+ nextPageButtonProps,
5090
5111
  ref: forwardedRef
5091
5112
  }
5092
5113
  ) : /* @__PURE__ */ jsx(
@@ -10601,6 +10622,117 @@ function useKeyboardShortcut(keybinds, action, {
10601
10622
  function ignoreFormControlsKeyboardShortcut(event) {
10602
10623
  return event.target !== null && event.target instanceof HTMLElement && FORM_CONTROLS.includes(event.target.tagName);
10603
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
+ }
10604
10736
  function useIntersectionObserver(element, onIntersectionEntryChange, options) {
10605
10737
  useEffect(() => {
10606
10738
  if (element !== null) {
@@ -10832,6 +10964,7 @@ export {
10832
10964
  useLatestValues,
10833
10965
  useLayoutEffect,
10834
10966
  useLocale,
10967
+ useLocation,
10835
10968
  useMeasure,
10836
10969
  useMediaBreakpointDown,
10837
10970
  useMediaBreakpointUp,
@@ -10843,6 +10976,8 @@ export {
10843
10976
  useResponsiveValues,
10844
10977
  useScrollPosition,
10845
10978
  useScrollbarSize,
10979
+ useSearchParam,
10980
+ useSearchParams,
10846
10981
  useSetFieldControl,
10847
10982
  useSetFieldControlFocused,
10848
10983
  useSetFieldFeedback,