@powerhousedao/design-system 6.2.3-dev.10 → 6.2.3-dev.11

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.
@@ -8086,6 +8086,14 @@ const PackageManagerListItem = (props) => {
8086
8086
  updateTarget
8087
8087
  ]
8088
8088
  }),
8089
+ registryPackage.sharedDepWarnings && registryPackage.sharedDepWarnings.length > 0 && /* @__PURE__ */ jsxs("span", {
8090
+ className: "inline-flex items-center gap-1 rounded-full border border-warning/40 bg-warning/10 px-2 py-0.5 text-[11px] font-medium text-warning",
8091
+ title: registryPackage.sharedDepWarnings.join("\n"),
8092
+ children: [/* @__PURE__ */ jsx(Icon, {
8093
+ name: "Exclamation",
8094
+ size: 10
8095
+ }), "Shared deps out of date"]
8096
+ }),
8089
8097
  !(canPickVersion && hasVersionMetadata) && registryPackage.version && /* @__PURE__ */ jsxs("span", {
8090
8098
  className: "text-xs font-normal text-muted-foreground",
8091
8099
  children: ["v", registryPackage.version]
@@ -8659,20 +8667,20 @@ const DocumentState = (props) => {
8659
8667
  };
8660
8668
  //#endregion
8661
8669
  //#region src/connect/components/revision-history/header/scope.tsx
8670
+ function labelFor(scope) {
8671
+ return `${scope.charAt(0).toUpperCase()}${scope.slice(1)} scope`;
8672
+ }
8662
8673
  function Scope(props) {
8663
- const { value, onChange } = props;
8674
+ const { scopes, value, onChange } = props;
8664
8675
  return /* @__PURE__ */ jsx(ConnectSelect, {
8665
8676
  absolutePositionMenu: true,
8666
8677
  containerClassName: "z-10 w-fit rounded-lg bg-background text-xs text-muted-foreground",
8667
8678
  id: "scope select",
8668
8679
  itemClassName: "grid grid-cols-[auto,auto] gap-1 py-2 text-muted-foreground",
8669
- items: [{
8670
- displayValue: "Global scope",
8671
- value: "global"
8672
- }, {
8673
- displayValue: "Local scope",
8674
- value: "local"
8675
- }],
8680
+ items: scopes.map((scope) => ({
8681
+ displayValue: labelFor(scope),
8682
+ value: scope
8683
+ })),
8676
8684
  menuClassName: "min-w-0 text-muted-foreground",
8677
8685
  onChange,
8678
8686
  value
@@ -8681,7 +8689,7 @@ function Scope(props) {
8681
8689
  //#endregion
8682
8690
  //#region src/connect/components/revision-history/header/header.tsx
8683
8691
  function Header(props) {
8684
- const { title, docId, scope, onChangeScope, onClose, className, documentState, onCopyState, onCopyDocId, ...divProps } = props;
8692
+ const { title, docId, scope, scopes, onChangeScope, onClose, className, documentState, onCopyState, onCopyDocId, ...divProps } = props;
8685
8693
  return /* @__PURE__ */ jsxs("header", {
8686
8694
  className: twMerge("flex items-center justify-between bg-transparent", className),
8687
8695
  ...divProps,
@@ -8710,6 +8718,7 @@ function Header(props) {
8710
8718
  /* @__PURE__ */ jsx(Branch, {}),
8711
8719
  /* @__PURE__ */ jsx(Scope, {
8712
8720
  onChange: onChangeScope,
8721
+ scopes,
8713
8722
  value: scope
8714
8723
  })
8715
8724
  ]
@@ -9075,90 +9084,110 @@ function Day(props) {
9075
9084
  }
9076
9085
  //#endregion
9077
9086
  //#region src/connect/components/revision-history/timeline/timeline.tsx
9087
+ /** How far each row is indented from the timeline's vertical line. */
9088
+ const ROW_INSET = 16;
9089
+ /**
9090
+ * How far the line is inset from the scroll container's clip edge -- exactly
9091
+ * the amount the day marker hangs past it (24 pulled back from a 16 inset
9092
+ * leaves 8 on the wrong side of zero). Without this the ring is sliced in
9093
+ * half, since a scrolling box clips on both axes.
9094
+ */
9095
+ const DAY_MARKER_OVERHANG = 24 - ROW_INSET;
9096
+ /**
9097
+ * The revision timeline, windowed by row.
9098
+ *
9099
+ * The scroll element and the content are deliberately two different
9100
+ * elements. A virtualizer decides what is visible by comparing its scroll
9101
+ * element's viewport against the total content size, so a scroll element
9102
+ * sized to the content is a viewport that shows everything at once: it never
9103
+ * scrolls, every row is "visible", and the whole page renders while still
9104
+ * paying the virtualizer's bookkeeping. The outer element here is therefore
9105
+ * bounded and scrollable, and the inner sizer carries the full height that
9106
+ * the absolutely positioned rows are placed within.
9107
+ */
9078
9108
  function Timeline(props) {
9079
- const { localOperations, globalOperations, scope } = props;
9080
- const operations = scope === "local" ? localOperations : globalOperations;
9081
- const initialNumRowsToShow = 100;
9082
- const allRows = useMemo(() => makeRows(operations), [operations]);
9083
- const [scrollAmount, setScrollAmount] = useState(0);
9084
- const [numRowsToShow, setNumRowsToShow] = useState(initialNumRowsToShow);
9085
- const [rows, setRows] = useState(() => allRows.slice(0, numRowsToShow));
9109
+ const { operations, maxHeight = "70vh" } = props;
9110
+ const rows = useMemo(() => makeRows([...operations]), [operations]);
9086
9111
  const parentRef = useRef(null);
9087
- const hasNextPage = rows.length < allRows.length;
9088
9112
  const rowVirtualizer = useVirtualizer({
9089
9113
  count: rows.length,
9090
9114
  getScrollElement: () => parentRef.current,
9091
- estimateSize: (i) => allRows[i].height,
9092
- gap: 8
9115
+ estimateSize: (i) => rows[i]?.height ?? 0,
9116
+ gap: 8,
9117
+ overscan: 8
9093
9118
  });
9094
- useEffect(() => {
9095
- if (!hasNextPage) return;
9096
- const newNumRevisions = initialNumRowsToShow + Math.floor(scrollAmount / 46);
9097
- setNumRowsToShow((prev) => newNumRevisions > prev ? newNumRevisions : prev);
9098
- }, [scrollAmount, hasNextPage]);
9099
- useEffect(() => {
9100
- setRows(allRows.slice(0, numRowsToShow));
9101
- }, [allRows, numRowsToShow]);
9102
- const handleScroll = (e) => {
9103
- setScrollAmount((prev) => {
9104
- const n = prev + e.deltaY;
9105
- if (n < 0) return 0;
9106
- return n;
9107
- });
9108
- };
9109
- useEffect(() => {
9110
- window.addEventListener("wheel", handleScroll);
9111
- return () => {
9112
- window.removeEventListener("wheel", handleScroll);
9113
- };
9114
- }, []);
9115
9119
  return /* @__PURE__ */ jsx("div", {
9116
- className: "border-l border-border dark:border-none",
9120
+ "data-testid": "revision-timeline",
9117
9121
  ref: parentRef,
9118
9122
  style: {
9119
- height: `${rowVirtualizer.getTotalSize()}px`,
9123
+ maxHeight,
9124
+ overflowY: "auto",
9120
9125
  width: "100%",
9121
9126
  position: "relative"
9122
9127
  },
9123
- children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
9124
- const row = rows[virtualRow.index];
9125
- return /* @__PURE__ */ jsxs("div", {
9126
- style: {
9127
- position: "absolute",
9128
- top: 0,
9129
- left: 16,
9130
- width: "100%",
9131
- height: `${virtualRow.size}px`,
9132
- transform: `translateY(${virtualRow.start}px)`
9133
- },
9134
- children: [
9135
- row.type === "revision" && /* @__PURE__ */ createElement(Revision, {
9136
- ...row,
9137
- key: virtualRow.key
9138
- }),
9139
- row.type === "skip" && /* @__PURE__ */ jsx(Skip, { ...row }, virtualRow.key),
9140
- row.type === "day" && /* @__PURE__ */ jsx(Day, { ...row }, virtualRow.key)
9141
- ]
9142
- }, virtualRow.index);
9128
+ children: /* @__PURE__ */ jsx("div", {
9129
+ className: "border-l border-border dark:border-none",
9130
+ style: {
9131
+ height: `${rowVirtualizer.getTotalSize()}px`,
9132
+ marginLeft: DAY_MARKER_OVERHANG,
9133
+ position: "relative"
9134
+ },
9135
+ children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
9136
+ const row = rows[virtualRow.index];
9137
+ if (!row) return null;
9138
+ return /* @__PURE__ */ jsxs("div", {
9139
+ style: {
9140
+ position: "absolute",
9141
+ top: 0,
9142
+ left: ROW_INSET,
9143
+ right: 0,
9144
+ height: `${virtualRow.size}px`,
9145
+ transform: `translateY(${virtualRow.start}px)`
9146
+ },
9147
+ children: [
9148
+ row.type === "revision" && /* @__PURE__ */ jsx(Revision, { ...row }),
9149
+ row.type === "skip" && /* @__PURE__ */ jsx(Skip, { ...row }),
9150
+ row.type === "day" && /* @__PURE__ */ jsx(Day, { ...row })
9151
+ ]
9152
+ }, virtualRow.key);
9153
+ })
9143
9154
  })
9144
9155
  });
9145
9156
  }
9146
9157
  //#endregion
9147
9158
  //#region src/connect/components/revision-history/revision-history.tsx
9148
- function RevisionHistory(props) {
9149
- const { documentTitle, documentId, globalOperations, localOperations, onClose, itemsPerPage = 100, documentState, onCopyState, onCopyDocId } = props;
9150
- const [scope, setScope] = useState("global");
9151
- const visibleOperations = useMemo(() => {
9152
- return garbageCollect(sortOperations(scope === "global" ? globalOperations : localOperations)).sort((a, b) => b.index - a.index);
9159
+ /** The current, scoped props. */
9160
+ /**
9161
+ * Legacy props: the whole global and local history, toggled inside the
9162
+ * component.
9163
+ * @deprecated Pass one scope's `operations` with `scopes`/`scope`/`onScopeChange` and the loading props. Removed in the next major.
9164
+ */
9165
+ /**
9166
+ * The revision history panel. Pages arrive oldest first while the timeline
9167
+ * renders newest first, so rendering progressively would show operations in
9168
+ * the wrong order until the last page landed and would repaint the whole
9169
+ * timeline on every page in between. Instead the component keeps asking for
9170
+ * the next page, showing "Loading operations…" and a running count, until
9171
+ * the walk completes, then renders the pagination and timeline once.
9172
+ */
9173
+ function ScopedRevisionHistory(props) {
9174
+ const { documentTitle, documentId, operations, isLoading, hasNextPage, onLoadNextPage, scopes, scope, onScopeChange, onClose, itemsPerPage = 100, documentState, onCopyState, onCopyDocId, error } = props;
9175
+ useEffect(() => {
9176
+ if (!hasNextPage || isLoading || error) return;
9177
+ const handle = window.setTimeout(onLoadNextPage, 0);
9178
+ return () => window.clearTimeout(handle);
9153
9179
  }, [
9154
- globalOperations,
9155
- localOperations,
9156
- scope
9180
+ hasNextPage,
9181
+ isLoading,
9182
+ onLoadNextPage,
9183
+ error
9157
9184
  ]);
9185
+ const isWalking = !error && (hasNextPage || isLoading && operations.length === 0);
9186
+ const visibleOperations = useMemo(() => isWalking ? [] : garbageCollect(sortOperations([...operations])).sort((a, b) => b.index - a.index), [operations, isWalking]);
9158
9187
  const { pageItems, pages, goToPage, goToNextPage, goToPreviousPage, goToFirstPage, goToLastPage, hiddenNextPages, isNextPageAvailable, isPreviousPageAvailable } = usePagination(visibleOperations, { itemsPerPage });
9159
- function onChangeScope(scope) {
9188
+ function onChangeScope(nextScope) {
9160
9189
  goToFirstPage();
9161
- setScope(scope);
9190
+ onScopeChange(nextScope);
9162
9191
  }
9163
9192
  const PaginationComponent = visibleOperations.length > itemsPerPage ? /* @__PURE__ */ jsx("div", {
9164
9193
  className: "mt-4 flex w-full justify-end",
@@ -9178,38 +9207,76 @@ function RevisionHistory(props) {
9178
9207
  previousPageLabel: "Previous"
9179
9208
  })
9180
9209
  }) : /* @__PURE__ */ jsx("hr", { className: "h-12 border-none" });
9210
+ const hasOperations = visibleOperations.length > 0;
9181
9211
  return /* @__PURE__ */ jsx(ConnectTooltipProvider, { children: /* @__PURE__ */ jsxs("div", {
9182
9212
  className: "p-6",
9183
- children: [
9184
- /* @__PURE__ */ jsx(Header, {
9185
- docId: documentId,
9186
- onChangeScope,
9187
- onClose,
9188
- scope,
9189
- title: documentTitle,
9190
- documentState,
9191
- onCopyState,
9192
- onCopyDocId
9193
- }),
9213
+ children: [/* @__PURE__ */ jsx(Header, {
9214
+ docId: documentId,
9215
+ onChangeScope,
9216
+ onClose,
9217
+ scope,
9218
+ scopes,
9219
+ title: documentTitle,
9220
+ documentState,
9221
+ onCopyState,
9222
+ onCopyDocId
9223
+ }), isWalking ? /* @__PURE__ */ jsxs("div", {
9224
+ className: "mt-4 flex flex-col items-center rounded-md bg-background p-4",
9225
+ children: [/* @__PURE__ */ jsx("h3", {
9226
+ className: "my-40 text-foreground",
9227
+ children: "Loading operations…"
9228
+ }), operations.length > 0 && /* @__PURE__ */ jsxs("p", {
9229
+ className: "text-xs text-muted-foreground",
9230
+ children: [operations.length, " loaded so far"]
9231
+ })]
9232
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9194
9233
  PaginationComponent,
9195
- /* @__PURE__ */ jsx("div", {
9196
- className: "mt-4 flex justify-center rounded-md bg-background p-4",
9197
- children: visibleOperations.length > 0 ? /* @__PURE__ */ jsx("div", {
9234
+ /* @__PURE__ */ jsxs("div", {
9235
+ className: "mt-4 flex flex-col items-center rounded-md bg-background p-4",
9236
+ children: [error && /* @__PURE__ */ jsxs("p", {
9237
+ className: "mb-2 text-xs text-destructive",
9238
+ children: ["Could not load the rest of the history: ", error.message]
9239
+ }), hasOperations ? /* @__PURE__ */ jsx("div", {
9198
9240
  className: "grid grid-cols-[minmax(min-content,1018px)]",
9199
- children: /* @__PURE__ */ jsx(Timeline, {
9200
- globalOperations: scope === "global" ? pageItems : [],
9201
- localOperations: scope === "local" ? pageItems : [],
9202
- scope
9203
- })
9204
- }) : /* @__PURE__ */ jsx("h3", {
9241
+ children: /* @__PURE__ */ jsx(Timeline, { operations: pageItems })
9242
+ }) : error ? null : /* @__PURE__ */ jsx("h3", {
9205
9243
  className: "my-40 text-foreground",
9206
9244
  children: "This document has no recorded operations yet."
9207
- })
9245
+ })]
9208
9246
  }),
9209
9247
  PaginationComponent
9210
- ]
9248
+ ] })]
9211
9249
  }) });
9212
9250
  }
9251
+ const LEGACY_SCOPES = ["global", "local"];
9252
+ const noop = () => {};
9253
+ /** Toggles between the whole global and local history loaded up front. */
9254
+ function LegacyRevisionHistory(props) {
9255
+ const { globalOperations, localOperations, ...common } = props;
9256
+ const [scope, setScope] = useState("global");
9257
+ return /* @__PURE__ */ jsx(ScopedRevisionHistory, {
9258
+ ...common,
9259
+ operations: scope === "local" ? localOperations : globalOperations,
9260
+ isLoading: false,
9261
+ hasNextPage: false,
9262
+ onLoadNextPage: noop,
9263
+ scopes: LEGACY_SCOPES,
9264
+ scope,
9265
+ onScopeChange: setScope
9266
+ });
9267
+ }
9268
+ /**
9269
+ * The revision history panel. Pages arrive oldest first while the timeline
9270
+ * renders newest first, so it waits for the whole history to load before
9271
+ * rendering the timeline, showing "Loading operations…" and a running count
9272
+ * in the meantime.
9273
+ *
9274
+ * Also accepts the deprecated `globalOperations`/`localOperations` pair
9275
+ * (see `RevisionHistoryLegacyProps`), toggling between them internally.
9276
+ */
9277
+ function RevisionHistory(props) {
9278
+ return "globalOperations" in props ? /* @__PURE__ */ jsx(LegacyRevisionHistory, { ...props }) : /* @__PURE__ */ jsx(ScopedRevisionHistory, { ...props });
9279
+ }
9213
9280
  //#endregion
9214
9281
  //#region src/connect/components/search-bar/filter-item.tsx
9215
9282
  const FilterItem = (props) => {