瀏覽代碼

ui: move session review bottom padding

Remove bottom padding from the scroll wrapper and apply it to the accordion content instead.
David Hill 1 月之前
父節點
當前提交
0a3a3216db
共有 2 個文件被更改,包括 266 次插入264 次删除
  1. 1 1
      packages/app/src/pages/session/review-tab.tsx
  2. 265 263
      packages/ui/src/components/session-review.tsx

+ 1 - 1
packages/app/src/pages/session/review-tab.tsx

@@ -176,7 +176,7 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
       open={props.view().review.open()}
       onOpenChange={props.view().review.setOpen}
       classes={{
-        root: props.classes?.root ?? "pb-6 pr-3",
+        root: props.classes?.root ?? "pr-3",
         header: props.classes?.header ?? "px-3",
         container: props.classes?.container ?? "pl-3",
       }}

+ 265 - 263
packages/ui/src/components/session-review.tsx

@@ -621,279 +621,281 @@ export const SessionReview = (props: SessionReviewProps) => {
 
         <div data-slot="session-review-container" class={props.classes?.container}>
           <Show when={hasDiffs()} fallback={props.empty}>
-            <Accordion multiple value={open()} onChange={handleChange}>
-              <For each={files()}>
-                {(file) => {
-                  let wrapper: HTMLDivElement | undefined
-
-                  const diff = createMemo(() => diffs().get(file))
-                  const item = () => diff()!
-
-                  const expanded = createMemo(() => open().includes(file))
-                  const force = () => !!store.force[file]
-
-                  const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === file))
-                  const commentedLines = createMemo(() => comments().map((c) => c.selection))
-
-                  const beforeText = () => (typeof item().before === "string" ? item().before : "")
-                  const afterText = () => (typeof item().after === "string" ? item().after : "")
-                  const changedLines = () => item().additions + item().deletions
-                  const mediaKind = createMemo(() => mediaKindFromPath(file))
-
-                  const tooLarge = createMemo(() => {
-                    if (!expanded()) return false
-                    if (force()) return false
-                    if (mediaKind()) return false
-                    return changedLines() > MAX_DIFF_CHANGED_LINES
-                  })
-
-                  const isAdded = () =>
-                    item().status === "added" || (beforeText().length === 0 && afterText().length > 0)
-                  const isDeleted = () =>
-                    item().status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
-
-                  const selectedLines = createMemo(() => {
-                    const current = selection()
-                    if (!current || current.file !== file) return null
-                    return current.range
-                  })
-
-                  const draftRange = createMemo(() => {
-                    const current = commenting()
-                    if (!current || current.file !== file) return null
-                    return current.range
-                  })
-
-                  const commentsUi = createLineCommentController<SessionReviewComment>({
-                    comments,
-                    label: i18n.t("ui.lineComment.submit"),
-                    draftKey: () => file,
-                    state: {
-                      opened: () => {
-                        const current = opened()
-                        if (!current || current.file !== file) return null
-                        return current.id
+            <div class="pb-6">
+              <Accordion multiple value={open()} onChange={handleChange}>
+                <For each={files()}>
+                  {(file) => {
+                    let wrapper: HTMLDivElement | undefined
+
+                    const diff = createMemo(() => diffs().get(file))
+                    const item = () => diff()!
+
+                    const expanded = createMemo(() => open().includes(file))
+                    const force = () => !!store.force[file]
+
+                    const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === file))
+                    const commentedLines = createMemo(() => comments().map((c) => c.selection))
+
+                    const beforeText = () => (typeof item().before === "string" ? item().before : "")
+                    const afterText = () => (typeof item().after === "string" ? item().after : "")
+                    const changedLines = () => item().additions + item().deletions
+                    const mediaKind = createMemo(() => mediaKindFromPath(file))
+
+                    const tooLarge = createMemo(() => {
+                      if (!expanded()) return false
+                      if (force()) return false
+                      if (mediaKind()) return false
+                      return changedLines() > MAX_DIFF_CHANGED_LINES
+                    })
+
+                    const isAdded = () =>
+                      item().status === "added" || (beforeText().length === 0 && afterText().length > 0)
+                    const isDeleted = () =>
+                      item().status === "deleted" || (afterText().length === 0 && beforeText().length > 0)
+
+                    const selectedLines = createMemo(() => {
+                      const current = selection()
+                      if (!current || current.file !== file) return null
+                      return current.range
+                    })
+
+                    const draftRange = createMemo(() => {
+                      const current = commenting()
+                      if (!current || current.file !== file) return null
+                      return current.range
+                    })
+
+                    const commentsUi = createLineCommentController<SessionReviewComment>({
+                      comments,
+                      label: i18n.t("ui.lineComment.submit"),
+                      draftKey: () => file,
+                      state: {
+                        opened: () => {
+                          const current = opened()
+                          if (!current || current.file !== file) return null
+                          return current.id
+                        },
+                        setOpened: (id) => setOpened(id ? { file, id } : null),
+                        selected: selectedLines,
+                        setSelected: (range) => setSelection(range ? { file, range } : null),
+                        commenting: draftRange,
+                        setCommenting: (range) => setCommenting(range ? { file, range } : null),
                       },
-                      setOpened: (id) => setOpened(id ? { file, id } : null),
-                      selected: selectedLines,
-                      setSelected: (range) => setSelection(range ? { file, range } : null),
-                      commenting: draftRange,
-                      setCommenting: (range) => setCommenting(range ? { file, range } : null),
-                    },
-                    getSide: selectionSide,
-                    clearSelectionOnSelectionEndNull: false,
-                    onSubmit: ({ comment, selection }) => {
-                      props.onLineComment?.({
-                        file,
-                        selection,
-                        comment,
-                        preview: selectionPreview(item(), selection),
-                      })
-                    },
-                    onUpdate: ({ id, comment, selection }) => {
-                      props.onLineCommentUpdate?.({
-                        id,
-                        file,
-                        selection,
-                        comment,
-                        preview: selectionPreview(item(), selection),
-                      })
-                    },
-                    onDelete: (comment) => {
-                      props.onLineCommentDelete?.({
-                        id: comment.id,
-                        file,
-                      })
-                    },
-                    editSubmitLabel: props.lineCommentActions?.saveLabel,
-                    renderCommentActions: props.lineCommentActions
-                      ? (comment, controls) => (
-                          <ReviewCommentMenu
-                            labels={props.lineCommentActions!}
-                            onEdit={controls.edit}
-                            onDelete={controls.remove}
-                          />
-                        )
-                      : undefined,
-                  })
-
-                  onCleanup(() => {
-                    anchors.delete(file)
-                    readyFiles.delete(file)
-                    searchHandles.delete(file)
-                    if (highlightedFile === file) highlightedFile = undefined
-                  })
-
-                  const handleLineSelected = (range: SelectedLineRange | null) => {
-                    if (!props.onLineComment) return
-                    commentsUi.onLineSelected(range)
-                  }
-
-                  const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
-                    if (!props.onLineComment) return
-                    commentsUi.onLineSelectionEnd(range)
-                  }
-
-                  return (
-                    <Accordion.Item
-                      value={file}
-                      id={diffId(file)}
-                      data-file={file}
-                      data-slot="session-review-accordion-item"
-                      data-selected={props.focusedFile === file ? "" : undefined}
-                    >
-                      <StickyAccordionHeader>
-                        <Accordion.Trigger>
-                          <div data-slot="session-review-trigger-content">
-                            <div data-slot="session-review-file-info">
-                              <FileIcon node={{ path: file, type: "file" }} />
-                              <div data-slot="session-review-file-name-container">
-                                <Show when={file.includes("/")}>
-                                  <span data-slot="session-review-directory">{`\u202A${getDirectory(file)}\u202C`}</span>
-                                </Show>
-                                <span data-slot="session-review-filename">{getFilename(file)}</span>
-                                <Show when={props.onViewFile}>
-                                  <Tooltip value={openFileLabel()} placement="top" gutter={4}>
-                                    <button
-                                      data-slot="session-review-view-button"
-                                      type="button"
-                                      aria-label={openFileLabel()}
-                                      onClick={(e) => {
-                                        e.stopPropagation()
-                                        props.onViewFile?.(file)
-                                      }}
-                                    >
-                                      <Icon name="open-file" size="small" />
-                                    </button>
-                                  </Tooltip>
-                                </Show>
+                      getSide: selectionSide,
+                      clearSelectionOnSelectionEndNull: false,
+                      onSubmit: ({ comment, selection }) => {
+                        props.onLineComment?.({
+                          file,
+                          selection,
+                          comment,
+                          preview: selectionPreview(item(), selection),
+                        })
+                      },
+                      onUpdate: ({ id, comment, selection }) => {
+                        props.onLineCommentUpdate?.({
+                          id,
+                          file,
+                          selection,
+                          comment,
+                          preview: selectionPreview(item(), selection),
+                        })
+                      },
+                      onDelete: (comment) => {
+                        props.onLineCommentDelete?.({
+                          id: comment.id,
+                          file,
+                        })
+                      },
+                      editSubmitLabel: props.lineCommentActions?.saveLabel,
+                      renderCommentActions: props.lineCommentActions
+                        ? (comment, controls) => (
+                            <ReviewCommentMenu
+                              labels={props.lineCommentActions!}
+                              onEdit={controls.edit}
+                              onDelete={controls.remove}
+                            />
+                          )
+                        : undefined,
+                    })
+
+                    onCleanup(() => {
+                      anchors.delete(file)
+                      readyFiles.delete(file)
+                      searchHandles.delete(file)
+                      if (highlightedFile === file) highlightedFile = undefined
+                    })
+
+                    const handleLineSelected = (range: SelectedLineRange | null) => {
+                      if (!props.onLineComment) return
+                      commentsUi.onLineSelected(range)
+                    }
+
+                    const handleLineSelectionEnd = (range: SelectedLineRange | null) => {
+                      if (!props.onLineComment) return
+                      commentsUi.onLineSelectionEnd(range)
+                    }
+
+                    return (
+                      <Accordion.Item
+                        value={file}
+                        id={diffId(file)}
+                        data-file={file}
+                        data-slot="session-review-accordion-item"
+                        data-selected={props.focusedFile === file ? "" : undefined}
+                      >
+                        <StickyAccordionHeader>
+                          <Accordion.Trigger>
+                            <div data-slot="session-review-trigger-content">
+                              <div data-slot="session-review-file-info">
+                                <FileIcon node={{ path: file, type: "file" }} />
+                                <div data-slot="session-review-file-name-container">
+                                  <Show when={file.includes("/")}>
+                                    <span data-slot="session-review-directory">{`\u202A${getDirectory(file)}\u202C`}</span>
+                                  </Show>
+                                  <span data-slot="session-review-filename">{getFilename(file)}</span>
+                                  <Show when={props.onViewFile}>
+                                    <Tooltip value={openFileLabel()} placement="top" gutter={4}>
+                                      <button
+                                        data-slot="session-review-view-button"
+                                        type="button"
+                                        aria-label={openFileLabel()}
+                                        onClick={(e) => {
+                                          e.stopPropagation()
+                                          props.onViewFile?.(file)
+                                        }}
+                                      >
+                                        <Icon name="open-file" size="small" />
+                                      </button>
+                                    </Tooltip>
+                                  </Show>
+                                </div>
                               </div>
-                            </div>
-                            <div data-slot="session-review-trigger-actions">
-                              <Switch>
-                                <Match when={isAdded()}>
-                                  <div data-slot="session-review-change-group" data-type="added">
-                                    <span data-slot="session-review-change" data-type="added">
-                                      {i18n.t("ui.sessionReview.change.added")}
+                              <div data-slot="session-review-trigger-actions">
+                                <Switch>
+                                  <Match when={isAdded()}>
+                                    <div data-slot="session-review-change-group" data-type="added">
+                                      <span data-slot="session-review-change" data-type="added">
+                                        {i18n.t("ui.sessionReview.change.added")}
+                                      </span>
+                                      <DiffChanges changes={item()} />
+                                    </div>
+                                  </Match>
+                                  <Match when={isDeleted()}>
+                                    <span data-slot="session-review-change" data-type="removed">
+                                      {i18n.t("ui.sessionReview.change.removed")}
+                                    </span>
+                                  </Match>
+                                  <Match when={!!mediaKind()}>
+                                    <span data-slot="session-review-change" data-type="modified">
+                                      {i18n.t("ui.sessionReview.change.modified")}
                                     </span>
+                                  </Match>
+                                  <Match when={true}>
                                     <DiffChanges changes={item()} />
+                                  </Match>
+                                </Switch>
+                                <span data-slot="session-review-diff-chevron">
+                                  <Icon name="chevron-down" size="small" />
+                                </span>
+                              </div>
+                            </div>
+                          </Accordion.Trigger>
+                        </StickyAccordionHeader>
+                        <Accordion.Content data-slot="session-review-accordion-content">
+                          <div
+                            data-slot="session-review-diff-wrapper"
+                            ref={(el) => {
+                              wrapper = el
+                              anchors.set(file, el)
+                            }}
+                          >
+                            <Show when={expanded()}>
+                              <Switch>
+                                <Match when={tooLarge()}>
+                                  <div data-slot="session-review-large-diff">
+                                    <div data-slot="session-review-large-diff-title">
+                                      {i18n.t("ui.sessionReview.largeDiff.title")}
+                                    </div>
+                                    <div data-slot="session-review-large-diff-meta">
+                                      {i18n.t("ui.sessionReview.largeDiff.meta", {
+                                        limit: MAX_DIFF_CHANGED_LINES.toLocaleString(),
+                                        current: changedLines().toLocaleString(),
+                                      })}
+                                    </div>
+                                    <div data-slot="session-review-large-diff-actions">
+                                      <Button
+                                        size="normal"
+                                        variant="secondary"
+                                        onClick={() => setStore("force", file, true)}
+                                      >
+                                        {i18n.t("ui.sessionReview.largeDiff.renderAnyway")}
+                                      </Button>
+                                    </div>
                                   </div>
                                 </Match>
-                                <Match when={isDeleted()}>
-                                  <span data-slot="session-review-change" data-type="removed">
-                                    {i18n.t("ui.sessionReview.change.removed")}
-                                  </span>
-                                </Match>
-                                <Match when={!!mediaKind()}>
-                                  <span data-slot="session-review-change" data-type="modified">
-                                    {i18n.t("ui.sessionReview.change.modified")}
-                                  </span>
-                                </Match>
                                 <Match when={true}>
-                                  <DiffChanges changes={item()} />
+                                  <Dynamic
+                                    component={fileComponent}
+                                    mode="diff"
+                                    preloadedDiff={item().preloaded}
+                                    diffStyle={diffStyle()}
+                                    expansionLineCount={searchExpanded() ? Number.MAX_SAFE_INTEGER : 20}
+                                    onRendered={() => {
+                                      readyFiles.add(file)
+                                      props.onDiffRendered?.()
+                                    }}
+                                    enableLineSelection={props.onLineComment != null}
+                                    enableHoverUtility={props.onLineComment != null}
+                                    onLineSelected={handleLineSelected}
+                                    onLineSelectionEnd={handleLineSelectionEnd}
+                                    onLineNumberSelectionEnd={commentsUi.onLineNumberSelectionEnd}
+                                    annotations={commentsUi.annotations()}
+                                    renderAnnotation={commentsUi.renderAnnotation}
+                                    renderHoverUtility={props.onLineComment ? commentsUi.renderHoverUtility : undefined}
+                                    selectedLines={selectedLines()}
+                                    commentedLines={commentedLines()}
+                                    search={{
+                                      shortcuts: "disabled",
+                                      showBar: false,
+                                      disableVirtualization: searchExpanded(),
+                                      register: (handle: FileSearchHandle | null) => {
+                                        if (!handle) {
+                                          searchHandles.delete(file)
+                                          readyFiles.delete(file)
+                                          if (highlightedFile === file) highlightedFile = undefined
+                                          return
+                                        }
+
+                                        searchHandles.set(file, handle)
+                                      },
+                                    }}
+                                    before={{
+                                      name: file,
+                                      contents: typeof item().before === "string" ? item().before : "",
+                                    }}
+                                    after={{
+                                      name: file,
+                                      contents: typeof item().after === "string" ? item().after : "",
+                                    }}
+                                    media={{
+                                      mode: "auto",
+                                      path: file,
+                                      before: item().before,
+                                      after: item().after,
+                                      readFile: props.readFile,
+                                    }}
+                                  />
                                 </Match>
                               </Switch>
-                              <span data-slot="session-review-diff-chevron">
-                                <Icon name="chevron-down" size="small" />
-                              </span>
-                            </div>
+                            </Show>
                           </div>
-                        </Accordion.Trigger>
-                      </StickyAccordionHeader>
-                      <Accordion.Content data-slot="session-review-accordion-content">
-                        <div
-                          data-slot="session-review-diff-wrapper"
-                          ref={(el) => {
-                            wrapper = el
-                            anchors.set(file, el)
-                          }}
-                        >
-                          <Show when={expanded()}>
-                            <Switch>
-                              <Match when={tooLarge()}>
-                                <div data-slot="session-review-large-diff">
-                                  <div data-slot="session-review-large-diff-title">
-                                    {i18n.t("ui.sessionReview.largeDiff.title")}
-                                  </div>
-                                  <div data-slot="session-review-large-diff-meta">
-                                    {i18n.t("ui.sessionReview.largeDiff.meta", {
-                                      limit: MAX_DIFF_CHANGED_LINES.toLocaleString(),
-                                      current: changedLines().toLocaleString(),
-                                    })}
-                                  </div>
-                                  <div data-slot="session-review-large-diff-actions">
-                                    <Button
-                                      size="normal"
-                                      variant="secondary"
-                                      onClick={() => setStore("force", file, true)}
-                                    >
-                                      {i18n.t("ui.sessionReview.largeDiff.renderAnyway")}
-                                    </Button>
-                                  </div>
-                                </div>
-                              </Match>
-                              <Match when={true}>
-                                <Dynamic
-                                  component={fileComponent}
-                                  mode="diff"
-                                  preloadedDiff={item().preloaded}
-                                  diffStyle={diffStyle()}
-                                  expansionLineCount={searchExpanded() ? Number.MAX_SAFE_INTEGER : 20}
-                                  onRendered={() => {
-                                    readyFiles.add(file)
-                                    props.onDiffRendered?.()
-                                  }}
-                                  enableLineSelection={props.onLineComment != null}
-                                  enableHoverUtility={props.onLineComment != null}
-                                  onLineSelected={handleLineSelected}
-                                  onLineSelectionEnd={handleLineSelectionEnd}
-                                  onLineNumberSelectionEnd={commentsUi.onLineNumberSelectionEnd}
-                                  annotations={commentsUi.annotations()}
-                                  renderAnnotation={commentsUi.renderAnnotation}
-                                  renderHoverUtility={props.onLineComment ? commentsUi.renderHoverUtility : undefined}
-                                  selectedLines={selectedLines()}
-                                  commentedLines={commentedLines()}
-                                  search={{
-                                    shortcuts: "disabled",
-                                    showBar: false,
-                                    disableVirtualization: searchExpanded(),
-                                    register: (handle: FileSearchHandle | null) => {
-                                      if (!handle) {
-                                        searchHandles.delete(file)
-                                        readyFiles.delete(file)
-                                        if (highlightedFile === file) highlightedFile = undefined
-                                        return
-                                      }
-
-                                      searchHandles.set(file, handle)
-                                    },
-                                  }}
-                                  before={{
-                                    name: file,
-                                    contents: typeof item().before === "string" ? item().before : "",
-                                  }}
-                                  after={{
-                                    name: file,
-                                    contents: typeof item().after === "string" ? item().after : "",
-                                  }}
-                                  media={{
-                                    mode: "auto",
-                                    path: file,
-                                    before: item().before,
-                                    after: item().after,
-                                    readFile: props.readFile,
-                                  }}
-                                />
-                              </Match>
-                            </Switch>
-                          </Show>
-                        </div>
-                      </Accordion.Content>
-                    </Accordion.Item>
-                  )
-                }}
-              </For>
-            </Accordion>
+                        </Accordion.Content>
+                      </Accordion.Item>
+                    )
+                  }}
+                </For>
+              </Accordion>
+            </div>
           </Show>
         </div>
       </ScrollView>