@geomak/ui 6.26.0 → 6.27.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.cjs CHANGED
@@ -2947,6 +2947,7 @@ function Scheduler({
2947
2947
  onSelectSlot,
2948
2948
  onSelectEvent,
2949
2949
  onNewEvent,
2950
+ onError,
2950
2951
  className = "",
2951
2952
  style
2952
2953
  }) {
@@ -2955,9 +2956,11 @@ function Scheduler({
2955
2956
  const [cursor, setCursor] = React29.useState(() => defaultDate ?? /* @__PURE__ */ new Date());
2956
2957
  const [loaded, setLoaded] = React29.useState([]);
2957
2958
  const [loading, setLoading] = React29.useState(false);
2959
+ const [error, setError] = React29.useState(null);
2960
+ const [reloadKey, setReloadKey] = React29.useState(0);
2958
2961
  const [dir, setDir] = React29.useState(0);
2959
- const loaderRef = React29.useRef(loadEvents);
2960
- loaderRef.current = loadEvents;
2962
+ const cbRef = React29.useRef({ loadEvents, onError });
2963
+ cbRef.current = { loadEvents, onError };
2961
2964
  const range = React29.useMemo(
2962
2965
  () => view === "month" ? monthRange(cursor) : weekRange(cursor, weekStartsOn),
2963
2966
  [view, cursor, weekStartsOn]
@@ -2965,21 +2968,26 @@ function Scheduler({
2965
2968
  const fromKey = range.from.getTime();
2966
2969
  const toKey = range.to.getTime();
2967
2970
  React29.useEffect(() => {
2968
- const loader = loaderRef.current;
2971
+ const { loadEvents: loader, onError: onErr } = cbRef.current;
2969
2972
  if (!loader) return;
2970
2973
  let cancelled = false;
2971
2974
  setLoading(true);
2975
+ setError(null);
2972
2976
  Promise.resolve(loader({ from: new Date(fromKey), to: new Date(toKey) }, view)).then((evts) => {
2973
2977
  if (!cancelled) setLoaded(evts);
2974
- }).catch(() => {
2975
- if (!cancelled) setLoaded([]);
2978
+ }).catch((err) => {
2979
+ if (!cancelled) {
2980
+ setError(err ?? new Error("Failed to load events"));
2981
+ onErr?.(err);
2982
+ }
2976
2983
  }).finally(() => {
2977
2984
  if (!cancelled) setLoading(false);
2978
2985
  });
2979
2986
  return () => {
2980
2987
  cancelled = true;
2981
2988
  };
2982
- }, [fromKey, toKey, view]);
2989
+ }, [fromKey, toKey, view, reloadKey]);
2990
+ const retry = React29.useCallback(() => setReloadKey((k) => k + 1), []);
2983
2991
  const events = React29.useMemo(
2984
2992
  () => (controlledEvents ?? loaded).map(normalize),
2985
2993
  [controlledEvents, loaded]
@@ -3032,7 +3040,7 @@ function Scheduler({
3032
3040
  onNewEvent && /* @__PURE__ */ jsxRuntime.jsx(Button_default, { size: "sm", icon: /* @__PURE__ */ jsxRuntime.jsx(Plus, {}), content: "New event", onClick: onNewEvent })
3033
3041
  ] })
3034
3042
  ] }),
3035
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
3043
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 overflow-hidden", children: error ? /* @__PURE__ */ jsxRuntime.jsx(SchedulerError, { onRetry: retry }) : loadEvents && loading && events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(SchedulerSkeleton, { view }) : /* @__PURE__ */ jsxRuntime.jsx(
3036
3044
  framerMotion.motion.div,
3037
3045
  {
3038
3046
  initial: { opacity: 0, x: reduced ? 0 : dir * 24 },
@@ -3067,6 +3075,36 @@ function Scheduler({
3067
3075
  }
3068
3076
  );
3069
3077
  }
3078
+ function SchedulerSkeleton({ view }) {
3079
+ const bar = "rounded bg-background animate-pulse";
3080
+ if (view === "week") {
3081
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col p-3", "aria-hidden": "true", children: [
3082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 grid gap-2", style: { gridTemplateColumns: "3.5rem repeat(7, 1fr)" }, children: [
3083
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
3084
+ Array.from({ length: 7 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} mx-auto h-8 w-8 rounded-full` }, i))
3085
+ ] }),
3086
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col gap-3", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} h-6`, style: { width: `${60 + i * 13 % 35}%` } }, i)) })
3087
+ ] });
3088
+ }
3089
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col p-3", "aria-hidden": "true", children: [
3090
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 grid grid-cols-7 gap-2", children: Array.from({ length: 7 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} h-2.5` }, i)) }),
3091
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid flex-1 grid-cols-7 grid-rows-5 gap-2", children: Array.from({ length: 35 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3092
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} h-5 w-5 rounded-full` }),
3093
+ i % 3 === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} h-3` }),
3094
+ i % 5 === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${bar} h-3`, style: { width: "70%" } })
3095
+ ] }, i)) })
3096
+ ] });
3097
+ }
3098
+ function SchedulerError({ onRetry }) {
3099
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [
3100
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-10 w-10 items-center justify-center rounded-full text-status-error", style: { backgroundColor: "color-mix(in oklab, var(--color-error) 12%, var(--color-surface))" }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h16.94a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z" }) }) }),
3101
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3102
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground", children: "Couldn\u2019t load events" }),
3103
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-xs text-foreground-muted", children: "Something went wrong fetching this range." })
3104
+ ] }),
3105
+ /* @__PURE__ */ jsxRuntime.jsx(Button_default, { size: "sm", variant: "secondary", content: "Retry", onClick: onRetry })
3106
+ ] });
3107
+ }
3070
3108
  function MonthYearPicker({ label, cursor, onPick }) {
3071
3109
  const [open, setOpen] = React29.useState(false);
3072
3110
  const [viewYear, setViewYear] = React29.useState(cursor.getFullYear());
@@ -5766,48 +5804,45 @@ function Pagination({
5766
5804
  if (next) setPerPageKey(next.key);
5767
5805
  }
5768
5806
  }, [serverSide, options.perPage, picker]);
5769
- const navBtn = (icon, disabled, onClick) => /* @__PURE__ */ jsxRuntime.jsx(IconButton, { disabled, onClick, icon });
5770
- const chevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
5771
- const doubleChevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
5772
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center justify-end pt-2", children: [
5773
- navBtn(
5774
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: doubleChevronRight }),
5775
- activePage === 0,
5776
- () => onPageChange(0)
5777
- ),
5778
- navBtn(
5779
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rotate-180 inline-flex", children: chevronRight }),
5780
- activePage === 0,
5781
- () => activePage > 0 && onPageChange(activePage - 1)
5782
- ),
5783
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-surface-raised rounded-lg ml-2 mr-2 shadow-sm p-2 w-10 text-center select-none text-foreground", children: activePage + 1 }),
5784
- navBtn(
5785
- chevronRight,
5786
- activePage === maxPage,
5787
- () => activePage < maxPage && onPageChange(activePage + 1)
5788
- ),
5789
- navBtn(
5790
- doubleChevronRight,
5791
- activePage === maxPage,
5792
- () => onPageChange(maxPage)
5793
- ),
5794
- options.withPicker && /* @__PURE__ */ jsxRuntime.jsx(
5795
- Dropdown,
5796
- {
5797
- style: { width: 80, position: "relative", bottom: 4 },
5798
- hasSearch: false,
5799
- items: picker,
5800
- isMultiselect: false,
5801
- value: displayPerPageKey,
5802
- onChange: ({ target: { value } }) => {
5803
- if (Array.isArray(value)) return;
5804
- const numKey = typeof value === "number" ? value : Number(value);
5805
- if (!serverSide) setPerPageKey(numKey);
5806
- const opt = picker.find((o) => o.key === numKey);
5807
- onPerPageChange(opt?.label ?? opt?.value ?? numKey);
5807
+ const navBtn = (icon, disabled, onClick, title) => /* @__PURE__ */ jsxRuntime.jsx(IconButton, { type: "bordered", size: "sm", disabled, onClick, icon, title });
5808
+ const chevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
5809
+ const doubleChevronRight = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) });
5810
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-x-4 gap-y-3 pt-3", children: [
5811
+ options.withPicker && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mr-auto flex items-center gap-2", children: [
5812
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap text-xs text-foreground-muted", children: "Rows per page" }),
5813
+ /* @__PURE__ */ jsxRuntime.jsx(
5814
+ Dropdown,
5815
+ {
5816
+ size: "sm",
5817
+ style: { width: 76 },
5818
+ hasSearch: false,
5819
+ items: picker,
5820
+ isMultiselect: false,
5821
+ value: displayPerPageKey,
5822
+ onChange: ({ target: { value } }) => {
5823
+ if (Array.isArray(value)) return;
5824
+ const numKey = typeof value === "number" ? value : Number(value);
5825
+ if (!serverSide) setPerPageKey(numKey);
5826
+ const opt = picker.find((o) => o.key === numKey);
5827
+ onPerPageChange(opt?.label ?? opt?.value ?? numKey);
5828
+ }
5808
5829
  }
5809
- }
5810
- )
5830
+ )
5831
+ ] }),
5832
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
5833
+ navBtn(/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex rotate-180", children: doubleChevronRight }), activePage === 0, () => onPageChange(0), "First page"),
5834
+ navBtn(/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex rotate-180", children: chevronRight }), activePage === 0, () => activePage > 0 && onPageChange(activePage - 1), "Previous page"),
5835
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 text-sm tabular-nums text-foreground-secondary select-none", children: [
5836
+ activePage + 1,
5837
+ " ",
5838
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-foreground-muted", children: [
5839
+ "/ ",
5840
+ maxPage + 1
5841
+ ] })
5842
+ ] }),
5843
+ navBtn(chevronRight, activePage === maxPage, () => activePage < maxPage && onPageChange(activePage + 1), "Next page"),
5844
+ navBtn(doubleChevronRight, activePage === maxPage, () => onPageChange(maxPage), "Last page")
5845
+ ] })
5811
5846
  ] });
5812
5847
  }
5813
5848
  function Table({
@@ -8535,7 +8570,7 @@ function OtpInput({
8535
8570
  emit(valid.join(""));
8536
8571
  focusBox(valid.length);
8537
8572
  };
8538
- return /* @__PURE__ */ jsxRuntime.jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxRuntime.jsxs(React29__default.default.Fragment, { children: [
8573
+ return /* @__PURE__ */ jsxRuntime.jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxRuntime.jsxs(React29__default.default.Fragment, { children: [
8539
8574
  /* @__PURE__ */ jsxRuntime.jsx(
8540
8575
  "input",
8541
8576
  {
@@ -8946,9 +8981,9 @@ function DateRangePicker({
8946
8981
  {
8947
8982
  align: "start",
8948
8983
  sideOffset: 4,
8949
- className: "bg-surface text-foreground border border-border rounded-lg shadow-md z-50 p-3 flex gap-3 animate-in fade-in-0 zoom-in-95",
8984
+ className: "bg-surface text-foreground border border-border rounded-lg shadow-md z-50 p-3 flex flex-col gap-3 sm:flex-row max-w-[calc(100vw-1rem)] max-h-[calc(100vh-2rem)] overflow-auto animate-in fade-in-0 zoom-in-95",
8950
8985
  children: [
8951
- presets && presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1 pr-3 border-r border-border min-w-[120px]", children: presets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
8986
+ presets && presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1 min-w-[120px] sm:pr-3 sm:border-r sm:border-border", children: presets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
8952
8987
  "button",
8953
8988
  {
8954
8989
  type: "button",
@@ -8961,7 +8996,7 @@ function DateRangePicker({
8961
8996
  },
8962
8997
  p.label
8963
8998
  )) }),
8964
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
8999
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 sm:flex-row", children: [
8965
9000
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
8966
9001
  /* @__PURE__ */ jsxRuntime.jsx(
8967
9002
  "button",