@richardmcquiston01/dashboard-widgets-toolkit 0.6.0 → 0.8.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/README.md +6 -1
- package/dist/core.cjs +109 -19
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +108 -20
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +109 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +108 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +164 -66
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -1
- package/dist/react.d.ts +8 -1
- package/dist/react.js +164 -66
- package/dist/react.js.map +1 -1
- package/dist/{scale-D51ZGIF4.d.cts → scale-CrXxawQn.d.cts} +64 -5
- package/dist/{scale-D51ZGIF4.d.ts → scale-CrXxawQn.d.ts} +64 -5
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode, RefObject } from 'react';
|
|
2
|
-
import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, b as WidgetDefinition, c as DetailProviderOptions, d as DetailData, e as DetailQuery, L as Locale, f as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, g as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, R as ResolvedTableControls, h as TextWidgetData, i as WidgetData, j as ResolveOptions, k as WidgetContext, l as WidgetProviders } from './scale-
|
|
2
|
+
import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, b as WidgetDefinition, c as DetailProviderOptions, d as DetailData, e as DetailQuery, L as Locale, f as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, g as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, R as ResolvedTableControls, h as TextWidgetData, i as WidgetData, j as ResolveOptions, k as WidgetContext, l as WidgetProviders } from './scale-CrXxawQn.cjs';
|
|
3
3
|
|
|
4
4
|
interface WidgetCardProps {
|
|
5
5
|
readonly title: string;
|
|
@@ -230,6 +230,13 @@ interface DashboardProps extends WidgetSettings {
|
|
|
230
230
|
* restore; persist it. Without it the layout is read-only (no controls).
|
|
231
231
|
*/
|
|
232
232
|
readonly onLayoutChange?: (layout: DashboardLayout) => void;
|
|
233
|
+
/**
|
|
234
|
+
* Ignore widget `locked` settings, so locked widgets can be moved, hidden
|
|
235
|
+
* and minimized. For administrators editing the default layout: pass it
|
|
236
|
+
* only for roles you trust, and enforce locks again on the server for
|
|
237
|
+
* everyone else (`enforceLocks`). It never changes the locks themselves.
|
|
238
|
+
*/
|
|
239
|
+
readonly overrideLocks?: boolean;
|
|
233
240
|
readonly onRetry?: (key: string) => void;
|
|
234
241
|
/**
|
|
235
242
|
* Loads the full data for a widget's detail view (widgets whose definition
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode, RefObject } from 'react';
|
|
2
|
-
import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, b as WidgetDefinition, c as DetailProviderOptions, d as DetailData, e as DetailQuery, L as Locale, f as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, g as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, R as ResolvedTableControls, h as TextWidgetData, i as WidgetData, j as ResolveOptions, k as WidgetContext, l as WidgetProviders } from './scale-
|
|
2
|
+
import { D as DashboardWidget, W as WidgetSize, a as WidgetFill, G as GraphWidgetData, N as NiceDomain, b as WidgetDefinition, c as DetailProviderOptions, d as DetailData, e as DetailQuery, L as Locale, f as DashboardLayout, A as AlertListWidgetData, B as BarListWidgetData, g as GaugeWidgetData, K as KpiWidgetData, T as TableWidgetData, R as ResolvedTableControls, h as TextWidgetData, i as WidgetData, j as ResolveOptions, k as WidgetContext, l as WidgetProviders } from './scale-CrXxawQn.js';
|
|
3
3
|
|
|
4
4
|
interface WidgetCardProps {
|
|
5
5
|
readonly title: string;
|
|
@@ -230,6 +230,13 @@ interface DashboardProps extends WidgetSettings {
|
|
|
230
230
|
* restore; persist it. Without it the layout is read-only (no controls).
|
|
231
231
|
*/
|
|
232
232
|
readonly onLayoutChange?: (layout: DashboardLayout) => void;
|
|
233
|
+
/**
|
|
234
|
+
* Ignore widget `locked` settings, so locked widgets can be moved, hidden
|
|
235
|
+
* and minimized. For administrators editing the default layout: pass it
|
|
236
|
+
* only for roles you trust, and enforce locks again on the server for
|
|
237
|
+
* everyone else (`enforceLocks`). It never changes the locks themselves.
|
|
238
|
+
*/
|
|
239
|
+
readonly overrideLocks?: boolean;
|
|
233
240
|
readonly onRetry?: (key: string) => void;
|
|
234
241
|
/**
|
|
235
242
|
* Loads the full data for a widget's detail view (widgets whose definition
|
package/dist/react.js
CHANGED
|
@@ -1568,6 +1568,45 @@ function ResolvedWidgetCard({
|
|
|
1568
1568
|
);
|
|
1569
1569
|
}
|
|
1570
1570
|
|
|
1571
|
+
// src/core/definition.ts
|
|
1572
|
+
var NOT_LOCKED = Object.freeze({
|
|
1573
|
+
move: false,
|
|
1574
|
+
hide: false,
|
|
1575
|
+
minimize: false
|
|
1576
|
+
});
|
|
1577
|
+
var FULLY_LOCKED = Object.freeze({
|
|
1578
|
+
move: true,
|
|
1579
|
+
hide: true,
|
|
1580
|
+
minimize: true
|
|
1581
|
+
});
|
|
1582
|
+
function resolveWidgetLock(definition) {
|
|
1583
|
+
const setting = definition.locked;
|
|
1584
|
+
if (setting === true) return FULLY_LOCKED;
|
|
1585
|
+
if (setting === void 0 || setting === false) return NOT_LOCKED;
|
|
1586
|
+
return {
|
|
1587
|
+
move: setting.move === true,
|
|
1588
|
+
hide: setting.hide === true,
|
|
1589
|
+
minimize: setting.minimize === true
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
function sortDefinitions(definitions) {
|
|
1593
|
+
return definitions.map((definition, index) => ({ definition, index })).sort(
|
|
1594
|
+
(a, b) => (a.definition.sortOrder ?? 0) - (b.definition.sortOrder ?? 0) || a.index - b.index
|
|
1595
|
+
).map(({ definition }) => definition);
|
|
1596
|
+
}
|
|
1597
|
+
function isWidgetVisibleTo(definition, roles) {
|
|
1598
|
+
if (definition.active === false) {
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
if (definition.roles === void 0 || definition.roles.length === 0) {
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
if (roles === void 0) {
|
|
1605
|
+
return false;
|
|
1606
|
+
}
|
|
1607
|
+
return definition.roles.some((role) => roles.includes(role));
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1571
1610
|
// src/core/layout.ts
|
|
1572
1611
|
var EMPTY_LAYOUT = Object.freeze({
|
|
1573
1612
|
order: Object.freeze([]),
|
|
@@ -1579,9 +1618,10 @@ function bySortOrder(items) {
|
|
|
1579
1618
|
(a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
|
|
1580
1619
|
).map(({ item }) => item);
|
|
1581
1620
|
}
|
|
1582
|
-
function
|
|
1583
|
-
|
|
1584
|
-
|
|
1621
|
+
function isPinned(item) {
|
|
1622
|
+
return resolveWidgetLock(item).move;
|
|
1623
|
+
}
|
|
1624
|
+
function viewerOrder(visible, layout) {
|
|
1585
1625
|
const byKey = new Map(visible.map((d) => [d.key, d]));
|
|
1586
1626
|
const ordered = [];
|
|
1587
1627
|
for (const key of layout.order) {
|
|
@@ -1596,34 +1636,62 @@ function visibleWidgets(definitions, layout) {
|
|
|
1596
1636
|
}
|
|
1597
1637
|
return ordered;
|
|
1598
1638
|
}
|
|
1639
|
+
function placeAroundPinned(visible, free) {
|
|
1640
|
+
const slots = bySortOrder(visible).map(
|
|
1641
|
+
(item) => isPinned(item) ? item : void 0
|
|
1642
|
+
);
|
|
1643
|
+
let next = 0;
|
|
1644
|
+
return slots.map((pinned) => pinned ?? free[next++]);
|
|
1645
|
+
}
|
|
1646
|
+
function visibleWidgets(definitions, layout) {
|
|
1647
|
+
const hidden = new Set(layout.hidden);
|
|
1648
|
+
const visible = definitions.filter((d) => !hidden.has(d.key));
|
|
1649
|
+
const ordered = viewerOrder(visible, layout);
|
|
1650
|
+
if (!visible.some(isPinned)) return ordered;
|
|
1651
|
+
return placeAroundPinned(
|
|
1652
|
+
visible,
|
|
1653
|
+
ordered.filter((item) => !isPinned(item))
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1599
1656
|
function hiddenWidgets(definitions, layout) {
|
|
1600
1657
|
const hidden = new Set(layout.hidden);
|
|
1601
1658
|
return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
|
|
1602
1659
|
}
|
|
1603
1660
|
function moveWidget(definitions, layout, key, toIndex) {
|
|
1604
|
-
const current = visibleWidgets(definitions, layout)
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
if (fromIndex === -1 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex) {
|
|
1661
|
+
const current = visibleWidgets(definitions, layout);
|
|
1662
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1663
|
+
const mover = current[fromIndex];
|
|
1664
|
+
if (mover === void 0 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex || isPinned(mover) || isPinned(current[toIndex])) {
|
|
1609
1665
|
return layout;
|
|
1610
1666
|
}
|
|
1611
|
-
const
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1667
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1668
|
+
const free = current.filter((item) => !isPinned(item));
|
|
1669
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1670
|
+
const toFree = freeSlots.indexOf(toIndex);
|
|
1671
|
+
free.splice(fromFree, 1);
|
|
1672
|
+
free.splice(toFree, 0, mover);
|
|
1673
|
+
const next = placeAroundPinned(current, free);
|
|
1674
|
+
return { ...layout, order: next.map((d) => d.key) };
|
|
1675
|
+
}
|
|
1676
|
+
function freeSlotIndexes(ordered) {
|
|
1677
|
+
const slots = [];
|
|
1678
|
+
ordered.forEach((item, index) => {
|
|
1679
|
+
if (!isPinned(item)) slots.push(index);
|
|
1680
|
+
});
|
|
1681
|
+
return slots;
|
|
1615
1682
|
}
|
|
1616
1683
|
function moveWidgetBy(definitions, layout, key, offset) {
|
|
1617
|
-
const current = visibleWidgets(definitions, layout)
|
|
1618
|
-
|
|
1619
|
-
);
|
|
1620
|
-
const fromIndex = current.indexOf(key);
|
|
1684
|
+
const current = visibleWidgets(definitions, layout);
|
|
1685
|
+
const fromIndex = current.findIndex((d) => d.key === key);
|
|
1621
1686
|
if (fromIndex === -1) return layout;
|
|
1622
|
-
const
|
|
1687
|
+
const freeSlots = freeSlotIndexes(current);
|
|
1688
|
+
const fromFree = freeSlots.indexOf(fromIndex);
|
|
1689
|
+
if (fromFree === -1) return layout;
|
|
1690
|
+
const toFree = Math.max(
|
|
1623
1691
|
0,
|
|
1624
|
-
Math.min(
|
|
1692
|
+
Math.min(freeSlots.length - 1, fromFree + Math.trunc(offset))
|
|
1625
1693
|
);
|
|
1626
|
-
return moveWidget(definitions, layout, key,
|
|
1694
|
+
return moveWidget(definitions, layout, key, freeSlots[toFree]);
|
|
1627
1695
|
}
|
|
1628
1696
|
function isHidden(layout, key) {
|
|
1629
1697
|
return layout.hidden.includes(key);
|
|
@@ -1654,6 +1722,23 @@ function restoreWidget(layout, key) {
|
|
|
1654
1722
|
function toggleMinimized(layout, key) {
|
|
1655
1723
|
return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
|
|
1656
1724
|
}
|
|
1725
|
+
function enforceLocks(definitions, layout, options = {}) {
|
|
1726
|
+
if (options.overrideLocks === true) return layout;
|
|
1727
|
+
const noMove = /* @__PURE__ */ new Set();
|
|
1728
|
+
const noHide = /* @__PURE__ */ new Set();
|
|
1729
|
+
const noMinimize = /* @__PURE__ */ new Set();
|
|
1730
|
+
for (const definition of definitions) {
|
|
1731
|
+
const lock = resolveWidgetLock(definition);
|
|
1732
|
+
if (lock.move) noMove.add(definition.key);
|
|
1733
|
+
if (lock.hide) noHide.add(definition.key);
|
|
1734
|
+
if (lock.minimize) noMinimize.add(definition.key);
|
|
1735
|
+
}
|
|
1736
|
+
const without = (keys, drop) => keys.some((key) => drop.has(key)) ? keys.filter((key) => !drop.has(key)) : keys;
|
|
1737
|
+
const order = without(layout.order, noMove);
|
|
1738
|
+
const hidden = without(layout.hidden, noHide);
|
|
1739
|
+
const minimized = without(layout.minimized, noMinimize);
|
|
1740
|
+
return order === layout.order && hidden === layout.hidden && minimized === layout.minimized ? layout : { ...layout, order, hidden, minimized };
|
|
1741
|
+
}
|
|
1657
1742
|
function nextSort(current, column) {
|
|
1658
1743
|
if (current?.column !== column) return { column, direction: "asc" };
|
|
1659
1744
|
return current.direction === "asc" ? { column, direction: "desc" } : void 0;
|
|
@@ -2052,10 +2137,17 @@ function DetailHost({
|
|
|
2052
2137
|
}
|
|
2053
2138
|
) });
|
|
2054
2139
|
}
|
|
2140
|
+
function withoutLock(definition) {
|
|
2141
|
+
if (definition.locked === void 0) return definition;
|
|
2142
|
+
const copy = { ...definition };
|
|
2143
|
+
delete copy.locked;
|
|
2144
|
+
return copy;
|
|
2145
|
+
}
|
|
2055
2146
|
function DashboardInner({
|
|
2056
2147
|
widgets,
|
|
2057
|
-
layout = EMPTY_LAYOUT,
|
|
2148
|
+
layout: savedLayout = EMPTY_LAYOUT,
|
|
2058
2149
|
onLayoutChange,
|
|
2150
|
+
overrideLocks = false,
|
|
2059
2151
|
onRetry,
|
|
2060
2152
|
loadDetail,
|
|
2061
2153
|
onOpenDetail,
|
|
@@ -2064,7 +2156,10 @@ function DashboardInner({
|
|
|
2064
2156
|
}) {
|
|
2065
2157
|
const slot = useSlotClassName();
|
|
2066
2158
|
const { labels } = useWidgetSettings();
|
|
2067
|
-
const definitions = widgets.map(
|
|
2159
|
+
const definitions = widgets.map(
|
|
2160
|
+
(w) => overrideLocks ? withoutLock(w.definition) : w.definition
|
|
2161
|
+
);
|
|
2162
|
+
const layout = enforceLocks(definitions, savedLayout);
|
|
2068
2163
|
const byKey = new Map(
|
|
2069
2164
|
widgets.map((w) => [w.definition.key, w])
|
|
2070
2165
|
);
|
|
@@ -2137,32 +2232,45 @@ function DashboardInner({
|
|
|
2137
2232
|
if (widget === void 0) return null;
|
|
2138
2233
|
const key = definition.key;
|
|
2139
2234
|
const title = definition.title;
|
|
2235
|
+
const lock = resolveWidgetLock(definition);
|
|
2236
|
+
const canMoveEarlier = !lock.move && moveWidgetBy(definitions, layout, key, -1) !== layout;
|
|
2237
|
+
const canMoveLater = !lock.move && moveWidgetBy(definitions, layout, key, 1) !== layout;
|
|
2140
2238
|
const actions = editable ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2141
|
-
/* @__PURE__ */
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2239
|
+
!lock.move && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2240
|
+
/* @__PURE__ */ jsx(
|
|
2241
|
+
"button",
|
|
2242
|
+
{
|
|
2243
|
+
type: "button",
|
|
2244
|
+
className: slot(
|
|
2245
|
+
"button",
|
|
2246
|
+
"dwt-button",
|
|
2247
|
+
"dwt-icon-button"
|
|
2248
|
+
),
|
|
2249
|
+
"aria-label": labels.moveEarlier(title),
|
|
2250
|
+
title: labels.moveEarlier(title),
|
|
2251
|
+
disabled: index === 0 || !canMoveEarlier,
|
|
2252
|
+
onClick: () => change(moveWidgetBy(definitions, layout, key, -1)),
|
|
2253
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2191" })
|
|
2254
|
+
}
|
|
2255
|
+
),
|
|
2256
|
+
/* @__PURE__ */ jsx(
|
|
2257
|
+
"button",
|
|
2258
|
+
{
|
|
2259
|
+
type: "button",
|
|
2260
|
+
className: slot(
|
|
2261
|
+
"button",
|
|
2262
|
+
"dwt-button",
|
|
2263
|
+
"dwt-icon-button"
|
|
2264
|
+
),
|
|
2265
|
+
"aria-label": labels.moveLater(title),
|
|
2266
|
+
title: labels.moveLater(title),
|
|
2267
|
+
disabled: index === shown.length - 1 || !canMoveLater,
|
|
2268
|
+
onClick: () => change(moveWidgetBy(definitions, layout, key, 1)),
|
|
2269
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2193" })
|
|
2270
|
+
}
|
|
2271
|
+
)
|
|
2272
|
+
] }),
|
|
2273
|
+
!lock.hide && /* @__PURE__ */ jsx(
|
|
2166
2274
|
"button",
|
|
2167
2275
|
{
|
|
2168
2276
|
type: "button",
|
|
@@ -2186,7 +2294,7 @@ function DashboardInner({
|
|
|
2186
2294
|
...widthProps(twelve, definition),
|
|
2187
2295
|
minimized: isMinimized(layout, key),
|
|
2188
2296
|
...actions === void 0 ? {} : { actions },
|
|
2189
|
-
...editable ? {
|
|
2297
|
+
...editable && !lock.minimize ? {
|
|
2190
2298
|
onToggleMinimized: () => change(toggleMinimized(layout, key))
|
|
2191
2299
|
} : {},
|
|
2192
2300
|
...headingLevel === void 0 ? {} : { headingLevel },
|
|
@@ -2230,25 +2338,6 @@ function Dashboard({
|
|
|
2230
2338
|
);
|
|
2231
2339
|
}
|
|
2232
2340
|
|
|
2233
|
-
// src/core/definition.ts
|
|
2234
|
-
function sortDefinitions(definitions) {
|
|
2235
|
-
return definitions.map((definition, index) => ({ definition, index })).sort(
|
|
2236
|
-
(a, b) => (a.definition.sortOrder ?? 0) - (b.definition.sortOrder ?? 0) || a.index - b.index
|
|
2237
|
-
).map(({ definition }) => definition);
|
|
2238
|
-
}
|
|
2239
|
-
function isWidgetVisibleTo(definition, roles) {
|
|
2240
|
-
if (definition.active === false) {
|
|
2241
|
-
return false;
|
|
2242
|
-
}
|
|
2243
|
-
if (definition.roles === void 0 || definition.roles.length === 0) {
|
|
2244
|
-
return true;
|
|
2245
|
-
}
|
|
2246
|
-
if (roles === void 0) {
|
|
2247
|
-
return false;
|
|
2248
|
-
}
|
|
2249
|
-
return definition.roles.some((role) => roles.includes(role));
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
2341
|
// src/core/payload.ts
|
|
2253
2342
|
var WIDGET_KINDS = [
|
|
2254
2343
|
"TEXT",
|
|
@@ -2330,6 +2419,14 @@ function optionalNumber(record, field, path, problems) {
|
|
|
2330
2419
|
requireNumber(record, field, path, problems);
|
|
2331
2420
|
}
|
|
2332
2421
|
}
|
|
2422
|
+
function optionalSortValue(record, path, problems) {
|
|
2423
|
+
const value = record["value"];
|
|
2424
|
+
if (value !== void 0 && typeof value !== "string" && !(typeof value === "number" && Number.isFinite(value))) {
|
|
2425
|
+
problems.push(
|
|
2426
|
+
`${path}.value must be a string or a finite number, got ${describeType2(value)}.`
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2333
2430
|
function optionalBoolean(record, field, path, problems) {
|
|
2334
2431
|
const value = record[field];
|
|
2335
2432
|
if (value !== void 0 && typeof value !== "boolean") {
|
|
@@ -2433,6 +2530,7 @@ function checkPayloadFields(kind, data, problems) {
|
|
|
2433
2530
|
}
|
|
2434
2531
|
eachRecord(row, rowPath, problems, (cell, cellPath) => {
|
|
2435
2532
|
requireString(cell, "text", cellPath, problems);
|
|
2533
|
+
optionalSortValue(cell, cellPath, problems);
|
|
2436
2534
|
optionalUrl(
|
|
2437
2535
|
cell,
|
|
2438
2536
|
"href",
|