@powerhousedao/design-system 6.2.3-dev.1 → 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.
@@ -2617,10 +2617,16 @@ function ControlsContainerSlot(props) {
2617
2617
  //#endregion
2618
2618
  //#region src/connect/components/drive-auth-gate/drive-auth-gate.tsx
2619
2619
  function DriveAuthGate(props) {
2620
- const { mode = "login", onLogin, onLogout, className } = props;
2621
- return /* @__PURE__ */ jsx("div", {
2622
- className: twMerge("flex w-[28rem] max-w-[calc(100%-2rem)] flex-col items-center gap-4 rounded-3xl bg-background p-8 text-center shadow-2xl ring-1 ring-black/5 dark:ring-white/10", className),
2623
- children: mode === "login" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2620
+ const { mode = "login", onLogin, onLogout, onClose, className } = props;
2621
+ return /* @__PURE__ */ jsxs("div", {
2622
+ className: twMerge("relative flex w-[28rem] max-w-[calc(100%-2rem)] flex-col items-center gap-4 rounded-3xl bg-background p-8 text-center shadow-2xl ring-1 ring-black/5 dark:ring-white/10", className),
2623
+ children: [onClose ? /* @__PURE__ */ jsx("button", {
2624
+ type: "button",
2625
+ "aria-label": "Close",
2626
+ onClick: onClose,
2627
+ className: "absolute top-4 right-4 flex size-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2628
+ children: /* @__PURE__ */ jsx(CloseGlyph, {})
2629
+ }) : null, mode === "login" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2624
2630
  /* @__PURE__ */ jsx("h2", {
2625
2631
  className: "text-xl font-semibold text-foreground",
2626
2632
  children: "Log in to access this drive"
@@ -2654,7 +2660,21 @@ function DriveAuthGate(props) {
2654
2660
  className: "mt-2 flex items-center gap-2 rounded-lg border border-border bg-card px-6 py-2.5 text-sm font-semibold text-foreground shadow-sm transition-colors hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
2655
2661
  children: "Log out"
2656
2662
  })
2657
- ] })
2663
+ ] })]
2664
+ });
2665
+ }
2666
+ function CloseGlyph() {
2667
+ return /* @__PURE__ */ jsx("svg", {
2668
+ "aria-hidden": "true",
2669
+ focusable: "false",
2670
+ width: 14,
2671
+ height: 14,
2672
+ viewBox: "0 0 14 14",
2673
+ fill: "none",
2674
+ stroke: "currentColor",
2675
+ strokeWidth: 1.5,
2676
+ strokeLinecap: "round",
2677
+ children: /* @__PURE__ */ jsx("path", { d: "M2 2 L12 12 M12 2 L2 12" })
2658
2678
  });
2659
2679
  }
2660
2680
  //#endregion
@@ -4941,8 +4961,14 @@ function CreateDocumentWithTypeModal(props) {
4941
4961
  value: PLACEHOLDER_KEY,
4942
4962
  displayValue: "Select document type…"
4943
4963
  }, ...typeItems] : typeItems;
4964
+ const resetTimer = useRef(void 0);
4965
+ useEffect(() => () => {
4966
+ if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
4967
+ }, []);
4944
4968
  const resetAfterClose = useCallback(() => {
4945
- setTimeout(() => {
4969
+ if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
4970
+ resetTimer.current = setTimeout(() => {
4971
+ resetTimer.current = void 0;
4946
4972
  setDocumentName("");
4947
4973
  setIsNameValid(false);
4948
4974
  setSelectedKey(PLACEHOLDER_KEY);
@@ -8060,6 +8086,14 @@ const PackageManagerListItem = (props) => {
8060
8086
  updateTarget
8061
8087
  ]
8062
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
+ }),
8063
8097
  !(canPickVersion && hasVersionMetadata) && registryPackage.version && /* @__PURE__ */ jsxs("span", {
8064
8098
  className: "text-xs font-normal text-muted-foreground",
8065
8099
  children: ["v", registryPackage.version]
@@ -8633,20 +8667,20 @@ const DocumentState = (props) => {
8633
8667
  };
8634
8668
  //#endregion
8635
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
+ }
8636
8673
  function Scope(props) {
8637
- const { value, onChange } = props;
8674
+ const { scopes, value, onChange } = props;
8638
8675
  return /* @__PURE__ */ jsx(ConnectSelect, {
8639
8676
  absolutePositionMenu: true,
8640
8677
  containerClassName: "z-10 w-fit rounded-lg bg-background text-xs text-muted-foreground",
8641
8678
  id: "scope select",
8642
8679
  itemClassName: "grid grid-cols-[auto,auto] gap-1 py-2 text-muted-foreground",
8643
- items: [{
8644
- displayValue: "Global scope",
8645
- value: "global"
8646
- }, {
8647
- displayValue: "Local scope",
8648
- value: "local"
8649
- }],
8680
+ items: scopes.map((scope) => ({
8681
+ displayValue: labelFor(scope),
8682
+ value: scope
8683
+ })),
8650
8684
  menuClassName: "min-w-0 text-muted-foreground",
8651
8685
  onChange,
8652
8686
  value
@@ -8655,7 +8689,7 @@ function Scope(props) {
8655
8689
  //#endregion
8656
8690
  //#region src/connect/components/revision-history/header/header.tsx
8657
8691
  function Header(props) {
8658
- 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;
8659
8693
  return /* @__PURE__ */ jsxs("header", {
8660
8694
  className: twMerge("flex items-center justify-between bg-transparent", className),
8661
8695
  ...divProps,
@@ -8684,6 +8718,7 @@ function Header(props) {
8684
8718
  /* @__PURE__ */ jsx(Branch, {}),
8685
8719
  /* @__PURE__ */ jsx(Scope, {
8686
8720
  onChange: onChangeScope,
8721
+ scopes,
8687
8722
  value: scope
8688
8723
  })
8689
8724
  ]
@@ -9049,90 +9084,110 @@ function Day(props) {
9049
9084
  }
9050
9085
  //#endregion
9051
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
+ */
9052
9108
  function Timeline(props) {
9053
- const { localOperations, globalOperations, scope } = props;
9054
- const operations = scope === "local" ? localOperations : globalOperations;
9055
- const initialNumRowsToShow = 100;
9056
- const allRows = useMemo(() => makeRows(operations), [operations]);
9057
- const [scrollAmount, setScrollAmount] = useState(0);
9058
- const [numRowsToShow, setNumRowsToShow] = useState(initialNumRowsToShow);
9059
- const [rows, setRows] = useState(() => allRows.slice(0, numRowsToShow));
9109
+ const { operations, maxHeight = "70vh" } = props;
9110
+ const rows = useMemo(() => makeRows([...operations]), [operations]);
9060
9111
  const parentRef = useRef(null);
9061
- const hasNextPage = rows.length < allRows.length;
9062
9112
  const rowVirtualizer = useVirtualizer({
9063
9113
  count: rows.length,
9064
9114
  getScrollElement: () => parentRef.current,
9065
- estimateSize: (i) => allRows[i].height,
9066
- gap: 8
9115
+ estimateSize: (i) => rows[i]?.height ?? 0,
9116
+ gap: 8,
9117
+ overscan: 8
9067
9118
  });
9068
- useEffect(() => {
9069
- if (!hasNextPage) return;
9070
- const newNumRevisions = initialNumRowsToShow + Math.floor(scrollAmount / 46);
9071
- setNumRowsToShow((prev) => newNumRevisions > prev ? newNumRevisions : prev);
9072
- }, [scrollAmount, hasNextPage]);
9073
- useEffect(() => {
9074
- setRows(allRows.slice(0, numRowsToShow));
9075
- }, [allRows, numRowsToShow]);
9076
- const handleScroll = (e) => {
9077
- setScrollAmount((prev) => {
9078
- const n = prev + e.deltaY;
9079
- if (n < 0) return 0;
9080
- return n;
9081
- });
9082
- };
9083
- useEffect(() => {
9084
- window.addEventListener("wheel", handleScroll);
9085
- return () => {
9086
- window.removeEventListener("wheel", handleScroll);
9087
- };
9088
- }, []);
9089
9119
  return /* @__PURE__ */ jsx("div", {
9090
- className: "border-l border-border dark:border-none",
9120
+ "data-testid": "revision-timeline",
9091
9121
  ref: parentRef,
9092
9122
  style: {
9093
- height: `${rowVirtualizer.getTotalSize()}px`,
9123
+ maxHeight,
9124
+ overflowY: "auto",
9094
9125
  width: "100%",
9095
9126
  position: "relative"
9096
9127
  },
9097
- children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
9098
- const row = rows[virtualRow.index];
9099
- return /* @__PURE__ */ jsxs("div", {
9100
- style: {
9101
- position: "absolute",
9102
- top: 0,
9103
- left: 16,
9104
- width: "100%",
9105
- height: `${virtualRow.size}px`,
9106
- transform: `translateY(${virtualRow.start}px)`
9107
- },
9108
- children: [
9109
- row.type === "revision" && /* @__PURE__ */ createElement(Revision, {
9110
- ...row,
9111
- key: virtualRow.key
9112
- }),
9113
- row.type === "skip" && /* @__PURE__ */ jsx(Skip, { ...row }, virtualRow.key),
9114
- row.type === "day" && /* @__PURE__ */ jsx(Day, { ...row }, virtualRow.key)
9115
- ]
9116
- }, 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
+ })
9117
9154
  })
9118
9155
  });
9119
9156
  }
9120
9157
  //#endregion
9121
9158
  //#region src/connect/components/revision-history/revision-history.tsx
9122
- function RevisionHistory(props) {
9123
- const { documentTitle, documentId, globalOperations, localOperations, onClose, itemsPerPage = 100, documentState, onCopyState, onCopyDocId } = props;
9124
- const [scope, setScope] = useState("global");
9125
- const visibleOperations = useMemo(() => {
9126
- 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);
9127
9179
  }, [
9128
- globalOperations,
9129
- localOperations,
9130
- scope
9180
+ hasNextPage,
9181
+ isLoading,
9182
+ onLoadNextPage,
9183
+ error
9131
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]);
9132
9187
  const { pageItems, pages, goToPage, goToNextPage, goToPreviousPage, goToFirstPage, goToLastPage, hiddenNextPages, isNextPageAvailable, isPreviousPageAvailable } = usePagination(visibleOperations, { itemsPerPage });
9133
- function onChangeScope(scope) {
9188
+ function onChangeScope(nextScope) {
9134
9189
  goToFirstPage();
9135
- setScope(scope);
9190
+ onScopeChange(nextScope);
9136
9191
  }
9137
9192
  const PaginationComponent = visibleOperations.length > itemsPerPage ? /* @__PURE__ */ jsx("div", {
9138
9193
  className: "mt-4 flex w-full justify-end",
@@ -9152,38 +9207,76 @@ function RevisionHistory(props) {
9152
9207
  previousPageLabel: "Previous"
9153
9208
  })
9154
9209
  }) : /* @__PURE__ */ jsx("hr", { className: "h-12 border-none" });
9210
+ const hasOperations = visibleOperations.length > 0;
9155
9211
  return /* @__PURE__ */ jsx(ConnectTooltipProvider, { children: /* @__PURE__ */ jsxs("div", {
9156
9212
  className: "p-6",
9157
- children: [
9158
- /* @__PURE__ */ jsx(Header, {
9159
- docId: documentId,
9160
- onChangeScope,
9161
- onClose,
9162
- scope,
9163
- title: documentTitle,
9164
- documentState,
9165
- onCopyState,
9166
- onCopyDocId
9167
- }),
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: [
9168
9233
  PaginationComponent,
9169
- /* @__PURE__ */ jsx("div", {
9170
- className: "mt-4 flex justify-center rounded-md bg-background p-4",
9171
- 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", {
9172
9240
  className: "grid grid-cols-[minmax(min-content,1018px)]",
9173
- children: /* @__PURE__ */ jsx(Timeline, {
9174
- globalOperations: scope === "global" ? pageItems : [],
9175
- localOperations: scope === "local" ? pageItems : [],
9176
- scope
9177
- })
9178
- }) : /* @__PURE__ */ jsx("h3", {
9241
+ children: /* @__PURE__ */ jsx(Timeline, { operations: pageItems })
9242
+ }) : error ? null : /* @__PURE__ */ jsx("h3", {
9179
9243
  className: "my-40 text-foreground",
9180
9244
  children: "This document has no recorded operations yet."
9181
- })
9245
+ })]
9182
9246
  }),
9183
9247
  PaginationComponent
9184
- ]
9248
+ ] })]
9185
9249
  }) });
9186
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
+ }
9187
9280
  //#endregion
9188
9281
  //#region src/connect/components/search-bar/filter-item.tsx
9189
9282
  const FilterItem = (props) => {