@morze/ui 0.1.0 → 0.2.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.js CHANGED
@@ -217,7 +217,6 @@ function Button({
217
217
  asChild = false,
218
218
  tone,
219
219
  loading = false,
220
- dot = false,
221
220
  disabled,
222
221
  children,
223
222
  ...props
@@ -225,7 +224,6 @@ function Button({
225
224
  const Comp = asChild ? Slot.Root : "button";
226
225
  const isDisabled = disabled || loading;
227
226
  const decorate = (label) => /* @__PURE__ */ jsxs(Fragment, { children: [
228
- dot ? /* @__PURE__ */ jsx("span", { className: "mz-btn__dot", "aria-hidden": "true" }) : null,
229
227
  label,
230
228
  loading ? /* @__PURE__ */ jsx("span", { className: "mz-btn__spinner", "aria-hidden": "true", children: /* @__PURE__ */ jsx(SpinnerIcon, {}) }) : null
231
229
  ] });
@@ -269,7 +267,6 @@ function Badge({
269
267
  className,
270
268
  variant = "solid",
271
269
  tone,
272
- dot = false,
273
270
  asChild = false,
274
271
  children,
275
272
  ...props
@@ -283,10 +280,7 @@ function Badge({
283
280
  "data-tone": tone,
284
281
  className: cn(badgeVariants({ variant }), className),
285
282
  ...props,
286
- children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
287
- dot ? /* @__PURE__ */ jsx("span", { className: "mz-badge__dot", "aria-hidden": "true" }) : null,
288
- children
289
- ] })
283
+ children
290
284
  }
291
285
  );
292
286
  }
@@ -1824,6 +1818,8 @@ var defaultDataTableLabels = {
1824
1818
  previousPage: "Previous page",
1825
1819
  nextPage: "Next page",
1826
1820
  lastPage: "Last page",
1821
+ loadMore: "Load more",
1822
+ loadingMore: "Loading\u2026",
1827
1823
  selectedCount: (count) => `Selected: ${count}`,
1828
1824
  allMatchingSuffix: " (all matching)",
1829
1825
  selectAllMatching: (total) => `Select all ${total}`,
@@ -1848,6 +1844,8 @@ function isFilterActive(value) {
1848
1844
  return Boolean(value.from || value.to);
1849
1845
  case "boolean":
1850
1846
  return true;
1847
+ case "custom":
1848
+ return !(value.value === void 0 || value.value === null || value.value === "" || Array.isArray(value.value) && value.value.length === 0);
1851
1849
  }
1852
1850
  }
1853
1851
  function activeFilters(filters) {
@@ -1918,10 +1916,11 @@ function ColumnFilter({ columnLabel, def, value, onApply, labels: labelsProp })
1918
1916
  draft,
1919
1917
  setDraft,
1920
1918
  labels,
1921
- onSubmit: () => commit(draft)
1919
+ onSubmit: () => commit(draft),
1920
+ onCommit: commit
1922
1921
  }
1923
1922
  ),
1924
- /* @__PURE__ */ jsxs("div", { className: "mz-dt__filter-actions", children: [
1923
+ def.type === "custom" && def.actions === false ? null : /* @__PURE__ */ jsxs("div", { className: "mz-dt__filter-actions", children: [
1925
1924
  /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => commit(void 0), children: labels.reset }),
1926
1925
  /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => commit(draft), children: labels.apply })
1927
1926
  ] })
@@ -1933,7 +1932,8 @@ function FilterBody({
1933
1932
  draft,
1934
1933
  setDraft,
1935
1934
  labels,
1936
- onSubmit
1935
+ onSubmit,
1936
+ onCommit
1937
1937
  }) {
1938
1938
  const submitOnEnter = (event) => {
1939
1939
  if (event.key === "Enter") {
@@ -2041,6 +2041,17 @@ function FilterBody({
2041
2041
  )
2042
2042
  ] });
2043
2043
  }
2044
+ if (def.type === "custom") {
2045
+ const staged = draft?.type === "custom" ? draft : void 0;
2046
+ const wrap = (value, label) => value === void 0 ? void 0 : { type: "custom", value, label };
2047
+ return /* @__PURE__ */ jsx(Fragment, { children: def.render({
2048
+ value: staged?.value,
2049
+ label: staged?.label,
2050
+ onChange: (value, label) => setDraft(wrap(value, label)),
2051
+ commit: (value, label) => onCommit(wrap(value, label)),
2052
+ labels
2053
+ }) });
2054
+ }
2044
2055
  const current = draft?.type === "boolean" ? draft.value : void 0;
2045
2056
  return /* @__PURE__ */ jsx("div", { className: "mz-dt__filter-options", role: "radiogroup", children: [
2046
2057
  { value: true, label: def.trueLabel ?? labels.yes },
@@ -2183,6 +2194,7 @@ function DataTablePagination({
2183
2194
  onQueryChange
2184
2195
  }) {
2185
2196
  const labels = resolveLabels(labelsProp);
2197
+ const sizes = pageSizeOptions === false ? [] : pageSizeOptions;
2186
2198
  const pages = pageCount(total, query.pageSize);
2187
2199
  const from = total === 0 ? 0 : (query.page - 1) * query.pageSize + 1;
2188
2200
  const to = total === void 0 ? from + rowsOnPage - 1 : Math.min(query.page * query.pageSize, total);
@@ -2199,7 +2211,7 @@ function DataTablePagination({
2199
2211
  total.toLocaleString(locale)
2200
2212
  ] }) : null
2201
2213
  ] }) }),
2202
- /* @__PURE__ */ jsxs("div", { className: "mz-dt__pagination-size", children: [
2214
+ sizes.length > 1 ? /* @__PURE__ */ jsxs("div", { className: "mz-dt__pagination-size", children: [
2203
2215
  /* @__PURE__ */ jsx("span", { children: labels.rowsPerPage }),
2204
2216
  /* @__PURE__ */ jsxs(
2205
2217
  Select,
@@ -2208,11 +2220,11 @@ function DataTablePagination({
2208
2220
  onValueChange: (value) => onQueryChange({ ...query, pageSize: Number(value), page: 1 }),
2209
2221
  children: [
2210
2222
  /* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "mz-dt__pagination-select", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
2211
- /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: String(size), children: size }, size)) })
2223
+ /* @__PURE__ */ jsx(SelectContent, { children: sizes.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: String(size), children: size }, size)) })
2212
2224
  ]
2213
2225
  }
2214
2226
  )
2215
- ] }),
2227
+ ] }) : null,
2216
2228
  /* @__PURE__ */ jsxs("div", { className: "mz-dt__pagination-nav", children: [
2217
2229
  /* @__PURE__ */ jsxs("span", { className: "mz-dt__pagination-page", children: [
2218
2230
  query.page,
@@ -2598,6 +2610,8 @@ function DataTable({
2598
2610
  bulkActions,
2599
2611
  renderExpanded,
2600
2612
  onRowClick,
2613
+ rowClassName,
2614
+ rowProps,
2601
2615
  layout: controlledLayout,
2602
2616
  onLayoutChange,
2603
2617
  persistKey = null,
@@ -2607,6 +2621,10 @@ function DataTable({
2607
2621
  stickyHeader = true,
2608
2622
  emptyState,
2609
2623
  pageSizeOptions,
2624
+ pagination,
2625
+ onLoadMore,
2626
+ hasMore,
2627
+ autoLoadMore = true,
2610
2628
  labels: labelsProp,
2611
2629
  locale,
2612
2630
  caption,
@@ -2661,7 +2679,11 @@ function DataTable({
2661
2679
  if (!scroller) return;
2662
2680
  const update = () => {
2663
2681
  const max = scroller.scrollWidth - scroller.clientWidth;
2664
- setScrolled({ left: scroller.scrollLeft > 1, right: scroller.scrollLeft < max - 1 });
2682
+ const left = scroller.scrollLeft > 1;
2683
+ const right = scroller.scrollLeft < max - 1;
2684
+ setScrolled(
2685
+ (current) => current.left === left && current.right === right ? current : { left, right }
2686
+ );
2665
2687
  };
2666
2688
  update();
2667
2689
  scroller.addEventListener("scroll", update, { passive: true });
@@ -2672,6 +2694,34 @@ function DataTable({
2672
2694
  observer.disconnect();
2673
2695
  };
2674
2696
  }, [visible.length, data.length]);
2697
+ const moreRef = React39.useRef(null);
2698
+ const onLoadMoreRef = React39.useRef(onLoadMore);
2699
+ React39.useEffect(() => {
2700
+ onLoadMoreRef.current = onLoadMore;
2701
+ });
2702
+ const askedAt = React39.useRef(-1);
2703
+ const canLoadMore = Boolean(onLoadMore) && (hasMore ?? (total === void 0 || data.length < total));
2704
+ const showLoadMore = canLoadMore && !error && data.length > 0;
2705
+ const showPager = pagination ?? !onLoadMore;
2706
+ const askForMore = () => {
2707
+ if (askedAt.current === data.length) return;
2708
+ askedAt.current = data.length;
2709
+ onLoadMoreRef.current?.();
2710
+ };
2711
+ React39.useEffect(() => {
2712
+ const sentinel = moreRef.current;
2713
+ const scroller = scrollerRef.current;
2714
+ if (!sentinel || !scroller || !showLoadMore || !autoLoadMore || loading) return;
2715
+ if (typeof IntersectionObserver === "undefined") return;
2716
+ const observer = new IntersectionObserver(
2717
+ (entries) => {
2718
+ if (entries.some((entry) => entry.isIntersecting)) askForMore();
2719
+ },
2720
+ { root: scroller, rootMargin: "120px" }
2721
+ );
2722
+ observer.observe(sentinel);
2723
+ return () => observer.disconnect();
2724
+ }, [showLoadMore, autoLoadMore, loading, data.length]);
2675
2725
  const offsets = React39.useMemo(() => {
2676
2726
  const result = /* @__PURE__ */ new Map();
2677
2727
  let leftParts = selectable ? [`${SELECT_WIDTH}px`] : [];
@@ -2923,14 +2973,21 @@ function DataTable({
2923
2973
  /* @__PURE__ */ jsx("tbody", { children: loading && data.length === 0 ? Array.from({ length: Math.min(query.pageSize, 8) }, (_, index) => /* @__PURE__ */ jsx("tr", { className: "mz-dt__row", children: /* @__PURE__ */ jsx("td", { className: "mz-dt__td", colSpan, children: /* @__PURE__ */ jsx(Skeleton, { style: { height: 14, width: `${60 + index * 13 % 35}%` } }) }) }, `skeleton-${index}`)) : data.map((row, rowIndex) => {
2924
2974
  const key = rowKey(row);
2925
2975
  const isOpen = expanded.has(key);
2976
+ const context = { rowIndex };
2977
+ const hostClass = rowClassName?.(row, context);
2978
+ const hostProps = rowProps?.(row, context);
2926
2979
  return /* @__PURE__ */ jsxs(React39.Fragment, { children: [
2927
2980
  /* @__PURE__ */ jsxs(
2928
2981
  "tr",
2929
2982
  {
2930
- className: "mz-dt__row",
2983
+ ...hostProps,
2984
+ className: cn("mz-dt__row", hostClass, hostProps?.className),
2931
2985
  "data-selected": rows.selected.has(key) || rows.allMatching || void 0,
2932
2986
  "data-clickable": onRowClick ? true : void 0,
2933
- onClick: onRowClick ? () => onRowClick(row) : void 0,
2987
+ onClick: onRowClick || hostProps?.onClick ? (event) => {
2988
+ hostProps?.onClick?.(event);
2989
+ onRowClick?.(row);
2990
+ } : void 0,
2934
2991
  children: [
2935
2992
  selectable ? /* @__PURE__ */ jsx(
2936
2993
  "td",
@@ -2979,7 +3036,11 @@ function DataTable({
2979
3036
  ]
2980
3037
  }
2981
3038
  ),
2982
- isOpen && renderExpanded ? /* @__PURE__ */ jsx("tr", { className: "mz-dt__row mz-dt__row--expanded", children: /* @__PURE__ */ jsx("td", { className: "mz-dt__td mz-dt__td--expanded", colSpan, children: renderExpanded(row) }) }) : null
3039
+ isOpen && renderExpanded ? (
3040
+ // The detail panel belongs to the same record, so a row
3041
+ // tint carries onto it; the attributes do not.
3042
+ /* @__PURE__ */ jsx("tr", { className: cn("mz-dt__row mz-dt__row--expanded", hostClass), children: /* @__PURE__ */ jsx("td", { className: "mz-dt__td mz-dt__td--expanded", colSpan, children: renderExpanded(row) }) })
3043
+ ) : null
2983
3044
  ] }, key);
2984
3045
  }) })
2985
3046
  ] }),
@@ -3001,11 +3062,12 @@ function DataTable({
3001
3062
  /* @__PURE__ */ jsx("p", { children: error }),
3002
3063
  onRetry ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onRetry, children: labels.retry }) : null
3003
3064
  ] }) : null,
3065
+ showLoadMore ? /* @__PURE__ */ jsx("div", { ref: moreRef, className: "mz-dt__more", "data-slot": "data-table-load-more", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", disabled: loading, onClick: askForMore, children: loading ? labels.loadingMore : labels.loadMore }) }) : null,
3004
3066
  loading && data.length > 0 ? /* @__PURE__ */ jsx("div", { className: "mz-dt__loading-bar", "aria-hidden": "true" }) : null
3005
3067
  ]
3006
3068
  }
3007
3069
  ),
3008
- /* @__PURE__ */ jsx(
3070
+ showPager ? /* @__PURE__ */ jsx(
3009
3071
  DataTablePagination,
3010
3072
  {
3011
3073
  query,
@@ -3016,7 +3078,7 @@ function DataTable({
3016
3078
  locale,
3017
3079
  onQueryChange
3018
3080
  }
3019
- ),
3081
+ ) : null,
3020
3082
  selectable && (rows.count ?? 0) > 0 ? /* @__PURE__ */ jsxs("div", { className: "mz-dt__bulkbar", role: "region", "aria-label": labels.bulkActions, children: [
3021
3083
  /* @__PURE__ */ jsxs("span", { className: "mz-dt__bulkbar-count", children: [
3022
3084
  labels.selectedCount(
@@ -3051,6 +3113,10 @@ function describeFilter(value, def, labels) {
3051
3113
  return `: ${value.from ?? "\u2026"} \u2013 ${value.to ?? "\u2026"}`;
3052
3114
  case "boolean":
3053
3115
  return `: ${value.value ? labels.yes.toLowerCase() : labels.no.toLowerCase()}`;
3116
+ case "custom": {
3117
+ const text = (def?.type === "custom" ? def.describe?.(value.value) : void 0) ?? value.label;
3118
+ return text ? `: ${text}` : "";
3119
+ }
3054
3120
  }
3055
3121
  }
3056
3122