@richardmcquiston01/dashboard-widgets-toolkit 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/react.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { D as DashboardWidget, W as WidgetSize, G as GraphWidgetData, N as NiceDomain, L as Locale, a as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, b as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, c as TextWidgetData, d as WidgetData } from './scale-WlRozDCf.cjs';
2
+ import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, L as Locale, b as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, c as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, d as TextWidgetData, e as WidgetData } from './scale-Bhjc5Eqt.cjs';
3
3
 
4
4
  interface WidgetCardProps {
5
5
  readonly title: string;
@@ -17,6 +17,13 @@ interface WidgetCardProps {
17
17
  readonly onRetry?: () => void;
18
18
  readonly footer?: ReactNode;
19
19
  readonly size?: WidgetSize;
20
+ /** Fill free space in the grid row (height, width or both). */
21
+ readonly fill?: WidgetFill;
22
+ /**
23
+ * Explicit grid column span, set by `Dashboard` and `WidgetGrid` to give a
24
+ * width-filling card the columns left over in its row.
25
+ */
26
+ readonly columnSpan?: number;
20
27
  /** 2 to 6; default 2. */
21
28
  readonly headingLevel?: 2 | 3 | 4 | 5 | 6;
22
29
  readonly className?: string;
@@ -26,11 +33,13 @@ interface WidgetCardProps {
26
33
  * The frame every widget sits in: title, optional description and actions,
27
34
  * a minimise toggle, and loading, empty and error states.
28
35
  */
29
- declare function WidgetCard({ title, description, actions, minimized, onToggleMinimized, status, emptyText, error, onRetry, footer, size, headingLevel, className, children, }: WidgetCardProps): ReactNode;
36
+ declare function WidgetCard({ title, description, actions, minimized, onToggleMinimized, status, emptyText, error, onRetry, footer, size, fill, columnSpan, headingLevel, className, children, }: WidgetCardProps): ReactNode;
30
37
  /** The title to show: alert lists add their total, e.g. "Low stock (3)". */
31
38
  declare function widgetTitle(widget: DashboardWidget): string;
32
39
  interface ResolvedWidgetCardProps {
33
40
  readonly widget: DashboardWidget;
41
+ /** See `WidgetCardProps.columnSpan`. */
42
+ readonly columnSpan?: number;
34
43
  readonly actions?: ReactNode;
35
44
  readonly minimized?: boolean;
36
45
  readonly onToggleMinimized?: () => void;
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { D as DashboardWidget, W as WidgetSize, G as GraphWidgetData, N as NiceDomain, L as Locale, a as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, b as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, c as TextWidgetData, d as WidgetData } from './scale-WlRozDCf.js';
2
+ import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, L as Locale, b as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, c as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, d as TextWidgetData, e as WidgetData } from './scale-Bhjc5Eqt.js';
3
3
 
4
4
  interface WidgetCardProps {
5
5
  readonly title: string;
@@ -17,6 +17,13 @@ interface WidgetCardProps {
17
17
  readonly onRetry?: () => void;
18
18
  readonly footer?: ReactNode;
19
19
  readonly size?: WidgetSize;
20
+ /** Fill free space in the grid row (height, width or both). */
21
+ readonly fill?: WidgetFill;
22
+ /**
23
+ * Explicit grid column span, set by `Dashboard` and `WidgetGrid` to give a
24
+ * width-filling card the columns left over in its row.
25
+ */
26
+ readonly columnSpan?: number;
20
27
  /** 2 to 6; default 2. */
21
28
  readonly headingLevel?: 2 | 3 | 4 | 5 | 6;
22
29
  readonly className?: string;
@@ -26,11 +33,13 @@ interface WidgetCardProps {
26
33
  * The frame every widget sits in: title, optional description and actions,
27
34
  * a minimise toggle, and loading, empty and error states.
28
35
  */
29
- declare function WidgetCard({ title, description, actions, minimized, onToggleMinimized, status, emptyText, error, onRetry, footer, size, headingLevel, className, children, }: WidgetCardProps): ReactNode;
36
+ declare function WidgetCard({ title, description, actions, minimized, onToggleMinimized, status, emptyText, error, onRetry, footer, size, fill, columnSpan, headingLevel, className, children, }: WidgetCardProps): ReactNode;
30
37
  /** The title to show: alert lists add their total, e.g. "Low stock (3)". */
31
38
  declare function widgetTitle(widget: DashboardWidget): string;
32
39
  interface ResolvedWidgetCardProps {
33
40
  readonly widget: DashboardWidget;
41
+ /** See `WidgetCardProps.columnSpan`. */
42
+ readonly columnSpan?: number;
34
43
  readonly actions?: ReactNode;
35
44
  readonly minimized?: boolean;
36
45
  readonly onToggleMinimized?: () => void;
package/dist/react.js CHANGED
@@ -1,8 +1,52 @@
1
- import { createContext, useContext, useId, useState } from 'react';
1
+ import { createContext, useContext, useId, useState, useRef, useEffect, useMemo } from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/react/card.tsx
5
5
 
6
+ // src/core/grid.ts
7
+ function fillsWidth(fill) {
8
+ return fill === "width" || fill === "both";
9
+ }
10
+ function fillsHeight(fill) {
11
+ return fill === "height" || fill === "both";
12
+ }
13
+ function baseColumnSpan(size, columns, largeSpan = 2) {
14
+ if (size === "full") return columns;
15
+ if (size === "large") return Math.min(largeSpan, columns);
16
+ return 1;
17
+ }
18
+ function fillColumnSpans(items, columns, largeSpan = 2) {
19
+ const trackCount = Math.max(1, Math.floor(columns));
20
+ const spans = /* @__PURE__ */ new Map();
21
+ let row = [];
22
+ let used = 0;
23
+ function closeRow() {
24
+ const fillers = row.filter(({ item }) => fillsWidth(item.fill));
25
+ const leftover = trackCount - used;
26
+ if (fillers.length > 0) {
27
+ const share = Math.floor(leftover / fillers.length);
28
+ const remainder = leftover % fillers.length;
29
+ fillers.forEach(({ item, span }, index) => {
30
+ spans.set(item.key, span + share + (index < remainder ? 1 : 0));
31
+ });
32
+ }
33
+ row = [];
34
+ used = 0;
35
+ }
36
+ for (const item of items) {
37
+ const span = baseColumnSpan(
38
+ item.defaultSize,
39
+ trackCount,
40
+ largeSpan
41
+ );
42
+ if (used + span > trackCount && row.length > 0) closeRow();
43
+ row.push({ item, span });
44
+ used += span;
45
+ }
46
+ closeRow();
47
+ return spans;
48
+ }
49
+
6
50
  // src/core/format.ts
7
51
  var DEFAULT_CURRENCY = "USD";
8
52
  function localeArgument(locale) {
@@ -902,6 +946,8 @@ function WidgetCard({
902
946
  onRetry,
903
947
  footer,
904
948
  size,
949
+ fill,
950
+ columnSpan,
905
951
  headingLevel = 2,
906
952
  className,
907
953
  children
@@ -947,8 +993,11 @@ function WidgetCard({
947
993
  `dwt-card--${status}`,
948
994
  minimized && "dwt-card--minimized",
949
995
  size !== void 0 && `dwt-card--size-${size}`,
996
+ fillsHeight(fill) && "dwt-card--fill-height",
997
+ fillsWidth(fill) && "dwt-card--fill-width",
950
998
  className
951
999
  ),
1000
+ ...columnSpan === void 0 ? {} : { style: { gridColumn: `span ${columnSpan}` } },
952
1001
  children: [
953
1002
  /* @__PURE__ */ jsxs("header", { className: slot("cardHeader", "dwt-card-header"), children: [
954
1003
  /* @__PURE__ */ jsxs("div", { className: "dwt-card-heading", children: [
@@ -1012,6 +1061,7 @@ function ResolvedWidgetCard({
1012
1061
  status: widget.status,
1013
1062
  ...definition.description === void 0 ? {} : { description: definition.description },
1014
1063
  ...definition.defaultSize === void 0 ? {} : { size: definition.defaultSize },
1064
+ ...definition.fill === void 0 ? {} : { fill: definition.fill },
1015
1065
  ...widget.status === "empty" ? { emptyText: widget.emptyText } : {},
1016
1066
  ...widget.status === "error" ? { error: widget.error } : {},
1017
1067
  children: widget.status === "ok" ? /* @__PURE__ */ jsx(WidgetContent, { data: widget.data }) : null
@@ -1105,6 +1155,41 @@ function restoreWidget(layout, key) {
1105
1155
  function toggleMinimized(layout, key) {
1106
1156
  return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
1107
1157
  }
1158
+ var NARROW_VIEWPORT_QUERY = "(max-width: 640px)";
1159
+ function useFillSpans(definitions, gridRef) {
1160
+ const [metrics, setMetrics] = useState(void 0);
1161
+ const needsSpans = definitions.some(
1162
+ (definition) => fillsWidth(definition.fill)
1163
+ );
1164
+ useEffect(() => {
1165
+ const grid = gridRef.current;
1166
+ if (!needsSpans || grid === null || typeof ResizeObserver === "undefined") {
1167
+ setMetrics(void 0);
1168
+ return void 0;
1169
+ }
1170
+ function measure() {
1171
+ if (grid === null) return;
1172
+ const tracks = getComputedStyle(grid).gridTemplateColumns;
1173
+ const columns = tracks === "" || tracks === "none" ? 0 : tracks.trim().split(/\s+/).length;
1174
+ const largeSpan = window.matchMedia(NARROW_VIEWPORT_QUERY).matches ? 1 : 2;
1175
+ setMetrics(
1176
+ (previous) => columns === 0 ? void 0 : previous?.columns === columns && previous.largeSpan === largeSpan ? previous : { columns, largeSpan }
1177
+ );
1178
+ }
1179
+ measure();
1180
+ const observer = new ResizeObserver(measure);
1181
+ observer.observe(grid);
1182
+ return () => observer.disconnect();
1183
+ }, [needsSpans, gridRef]);
1184
+ return useMemo(
1185
+ () => metrics === void 0 ? /* @__PURE__ */ new Map() : fillColumnSpans(definitions, metrics.columns, metrics.largeSpan),
1186
+ [definitions, metrics]
1187
+ );
1188
+ }
1189
+ function spanProps(spans, key) {
1190
+ const columnSpan = spans.get(key);
1191
+ return columnSpan === void 0 ? {} : { columnSpan };
1192
+ }
1108
1193
  function WidgetGrid({
1109
1194
  widgets,
1110
1195
  onRetry,
@@ -1112,10 +1197,14 @@ function WidgetGrid({
1112
1197
  className
1113
1198
  }) {
1114
1199
  const slot = useSlotClassName();
1115
- return /* @__PURE__ */ jsx("div", { className: slot("grid", "dwt-grid", className), children: widgets.map((widget) => /* @__PURE__ */ jsx(
1200
+ const gridRef = useRef(null);
1201
+ const definitions = widgets.map((w) => w.definition);
1202
+ const spans = useFillSpans(definitions, gridRef);
1203
+ return /* @__PURE__ */ jsx("div", { ref: gridRef, className: slot("grid", "dwt-grid", className), children: widgets.map((widget) => /* @__PURE__ */ jsx(
1116
1204
  ResolvedWidgetCard,
1117
1205
  {
1118
1206
  widget,
1207
+ ...spanProps(spans, widget.definition.key),
1119
1208
  ...headingLevel === void 0 ? {} : { headingLevel },
1120
1209
  ...onRetry === void 0 || widget.status !== "error" ? {} : { onRetry: () => onRetry(widget.definition.key) }
1121
1210
  },
@@ -1139,6 +1228,8 @@ function DashboardInner({
1139
1228
  const visible = visibleWidgets(definitions, layout);
1140
1229
  const hidden = hiddenWidgets(definitions, layout);
1141
1230
  const editable = onLayoutChange !== void 0;
1231
+ const gridRef = useRef(null);
1232
+ const spans = useFillSpans(visible, gridRef);
1142
1233
  function change(next) {
1143
1234
  if (next !== layout) onLayoutChange?.(next);
1144
1235
  }
@@ -1160,7 +1251,7 @@ function DashboardInner({
1160
1251
  definition.key
1161
1252
  ))
1162
1253
  ] }),
1163
- visible.length === 0 ? /* @__PURE__ */ jsx("p", { className: slot("empty", "dwt-empty", "dwt-dashboard-empty"), children: definitions.length === 0 ? labels.noWidgets : labels.allHidden }) : /* @__PURE__ */ jsx("div", { className: slot("grid", "dwt-grid"), children: visible.map((definition, index) => {
1254
+ visible.length === 0 ? /* @__PURE__ */ jsx("p", { className: slot("empty", "dwt-empty", "dwt-dashboard-empty"), children: definitions.length === 0 ? labels.noWidgets : labels.allHidden }) : /* @__PURE__ */ jsx("div", { ref: gridRef, className: slot("grid", "dwt-grid"), children: visible.map((definition, index) => {
1164
1255
  const widget = byKey.get(
1165
1256
  definition.key
1166
1257
  );
@@ -1208,6 +1299,7 @@ function DashboardInner({
1208
1299
  ResolvedWidgetCard,
1209
1300
  {
1210
1301
  widget,
1302
+ ...spanProps(spans, key),
1211
1303
  minimized: isMinimized(layout, key),
1212
1304
  ...actions === void 0 ? {} : { actions },
1213
1305
  ...editable ? {