@geomak/ui 6.26.1 → 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());