mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-28 22:03:30 +03:00
UI: Lane and commit messages design (#3868)
* UI: Remove icons for commit groups * UI: added paddings * Added folding base row + styles tweek * Commits section: update styles * UI: commit card layout update * UI: empty commit message style * UI and refactor - Update files list UI - components rename - show author for upstream commits * UI updates - File list history list UI - Commit card UI * UI: Commits footer empty state update
This commit is contained in:
parent
c553215d5a
commit
e7fd4c4457
5
app/src/lib/assets/empty-state/commits-up-to-date.svg
Normal file
5
app/src/lib/assets/empty-state/commits-up-to-date.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="52" height="19" viewBox="0 0 52 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.27" d="M6.55211 4.63293L6.94893 3.73981C7.3872 2.7534 8.9086 2.7534 9.12223 3.73981L9.31564 4.63293C9.78195 6.78611 11.3386 8.51755 13.5344 9.32527L13.6717 9.37576C14.6032 9.71842 14.4339 10.9877 13.4243 11.3303C10.9947 12.155 8.98036 13.953 8.04643 16.1631L7.59063 17.2418C7.16345 18.2527 5.60253 18.2527 5.40558 17.2418L5.19543 16.1631C4.76485 13.953 3.15994 12.155 0.918145 11.3303C-0.0133752 10.9877 0.106365 9.71842 1.11592 9.37576L1.26469 9.32527C3.64443 8.51755 5.59544 6.78611 6.55211 4.63293Z" fill="var(--clr-scale-ntrl-60)"/>
|
||||
<path opacity="0.27" fill-rule="evenodd" clip-rule="evenodd" d="M34 5.78842L23.9212 15.9692L17 8.97796L19.7909 6.15878L23.9212 10.3309L31.2091 2.96924L34 5.78842Z" fill="var(--clr-scale-pop-50)"/>
|
||||
<path opacity="0.24" fill-rule="evenodd" clip-rule="evenodd" d="M41.1521 7.01939L39.8672 2.16203L42.0557 1.58311L43.3406 6.44047L45.5376 2.40866L47.5559 3.0727L44.9499 7.85514L49.7801 6.57741L50.2601 8.39186L45.4299 9.66959L50.0598 12.5379L48.6338 14.1131L44.7306 11.695L46.0155 16.5523L43.827 17.1312L42.5421 12.2739L40.3451 16.3057L38.3267 15.6417L40.9328 10.8592L36.1026 12.1369L35.6226 10.3225L40.4528 9.04477L35.8229 6.17644L37.2489 4.60127L41.1521 7.01939Z" fill="var(--clr-scale-ntrl-60)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -24,7 +24,7 @@
|
||||
.avatar {
|
||||
width: var(--size-16);
|
||||
height: var(--size-16);
|
||||
border-radius: var(--radius-m);
|
||||
border-radius: var(--radius-l);
|
||||
}
|
||||
|
||||
.local {
|
||||
|
@ -22,6 +22,8 @@
|
||||
let mergeUpstreamWarningDismissedCheckbox = false;
|
||||
|
||||
$: multiple = base ? base.upstreamCommits.length > 1 || base.upstreamCommits.length == 0 : false;
|
||||
|
||||
console.log(base);
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
|
@ -203,99 +203,101 @@
|
||||
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
|
||||
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
|
||||
|
||||
<div
|
||||
class="branch-card__dropzone-wrapper"
|
||||
use:dropzone={{
|
||||
hover: 'move-commit-dz-hover',
|
||||
active: 'move-commit-dz-active',
|
||||
accepts: acceptMoveCommit,
|
||||
onDrop: onCommitDrop,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
use:dropzone={{
|
||||
hover: 'cherrypick-dz-hover',
|
||||
active: 'cherrypick-dz-active',
|
||||
accepts: acceptCherrypick,
|
||||
onDrop: onCherrypicked,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
use:dropzone={{
|
||||
hover: 'lane-dz-hover',
|
||||
active: 'lane-dz-active',
|
||||
accepts: acceptBranchDrop,
|
||||
onDrop: onBranchDrop,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
>
|
||||
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
|
||||
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
|
||||
<DropzoneOverlay class="move-commit-dz-marker" label="Move here" />
|
||||
<div class="card">
|
||||
<div
|
||||
class="branch-card__dropzone-wrapper"
|
||||
use:dropzone={{
|
||||
hover: 'move-commit-dz-hover',
|
||||
active: 'move-commit-dz-active',
|
||||
accepts: acceptMoveCommit,
|
||||
onDrop: onCommitDrop,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
use:dropzone={{
|
||||
hover: 'cherrypick-dz-hover',
|
||||
active: 'cherrypick-dz-active',
|
||||
accepts: acceptCherrypick,
|
||||
onDrop: onCherrypicked,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
use:dropzone={{
|
||||
hover: 'lane-dz-hover',
|
||||
active: 'lane-dz-active',
|
||||
accepts: acceptBranchDrop,
|
||||
onDrop: onBranchDrop,
|
||||
disabled: isUnapplied
|
||||
}}
|
||||
>
|
||||
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
|
||||
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
|
||||
<DropzoneOverlay class="move-commit-dz-marker" label="Move here" />
|
||||
|
||||
{#if branch.files?.length > 0}
|
||||
<div class="card">
|
||||
<BranchFiles
|
||||
files={branch.files}
|
||||
{isUnapplied}
|
||||
showCheckboxes={$commitBoxOpen}
|
||||
allowMultiple
|
||||
bind:this={branchFiles}
|
||||
/>
|
||||
{#if branch.active && branch.conflicted}
|
||||
<div class="card-notifications">
|
||||
<InfoMessage noRadius filled outlined={false} style="error">
|
||||
<svelte:fragment slot="title">
|
||||
{#if branch.files.some((f) => f.conflicted)}
|
||||
This virtual branch conflicts with upstream changes. Please resolve all
|
||||
conflicts and commit before you can continue.
|
||||
{:else}
|
||||
Please commit your resolved conflicts to continue.
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if branch.active}
|
||||
<CommitDialog
|
||||
projectId={project.id}
|
||||
expanded={commitBoxOpen}
|
||||
on:action={(e) => {
|
||||
if (e.detail == 'generate-branch-name') {
|
||||
generateBranchName();
|
||||
}
|
||||
}}
|
||||
{#if branch.files?.length > 0}
|
||||
<div class="branch-card__files">
|
||||
<BranchFiles
|
||||
files={branch.files}
|
||||
{isUnapplied}
|
||||
showCheckboxes={$commitBoxOpen}
|
||||
allowMultiple
|
||||
bind:this={branchFiles}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if branch.commits.length == 0}
|
||||
<div class="new-branch card">
|
||||
<EmptyStatePlaceholder image={laneNewSvg} width="11rem">
|
||||
<svelte:fragment slot="title">This is a new branch</svelte:fragment>
|
||||
<svelte:fragment slot="caption">
|
||||
You can drag and drop files or parts of files here.
|
||||
</svelte:fragment>
|
||||
</EmptyStatePlaceholder>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="no-changes card" data-dnd-ignore>
|
||||
<EmptyStatePlaceholder image={noChangesSvg} width="11rem" hasBottomShift={false}>
|
||||
<svelte:fragment slot="caption"
|
||||
>No uncommitted changes on this branch</svelte:fragment
|
||||
>
|
||||
</EmptyStatePlaceholder>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if branch.active && branch.conflicted}
|
||||
<div class="card-notifications">
|
||||
<InfoMessage noRadius filled outlined={false} style="error">
|
||||
<svelte:fragment slot="title">
|
||||
{#if branch.files.some((f) => f.conflicted)}
|
||||
This virtual branch conflicts with upstream changes. Please resolve all
|
||||
conflicts and commit before you can continue.
|
||||
{:else}
|
||||
Please commit your resolved conflicts to continue.
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<CommitList {isUnapplied} />
|
||||
<BranchFooter {isUnapplied} />
|
||||
{#if branch.active}
|
||||
<CommitDialog
|
||||
projectId={project.id}
|
||||
expanded={commitBoxOpen}
|
||||
on:action={(e) => {
|
||||
if (e.detail == 'generate-branch-name') {
|
||||
generateBranchName();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if branch.commits.length == 0}
|
||||
<div class="new-branch">
|
||||
<EmptyStatePlaceholder image={laneNewSvg} width="11rem">
|
||||
<svelte:fragment slot="title">This is a new branch</svelte:fragment>
|
||||
<svelte:fragment slot="caption">
|
||||
You can drag and drop files or parts of files here.
|
||||
</svelte:fragment>
|
||||
</EmptyStatePlaceholder>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="no-changes" data-dnd-ignore>
|
||||
<EmptyStatePlaceholder image={noChangesSvg} width="11rem" hasBottomShift={false}>
|
||||
<svelte:fragment slot="caption"
|
||||
>No uncommitted changes on this branch</svelte:fragment
|
||||
>
|
||||
</EmptyStatePlaceholder>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<CommitList {isUnapplied} />
|
||||
<BranchFooter {isUnapplied} />
|
||||
</div>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<div class="divider-line">
|
||||
<Resizer
|
||||
viewport={rsViewport}
|
||||
direction="right"
|
||||
minWidth={340}
|
||||
minWidth={380}
|
||||
sticky
|
||||
defaultLineColor={$fileIdSelection.length == 1 ? 'transparent' : 'var(--clr-border-2)'}
|
||||
on:width={(e) => {
|
||||
@ -349,6 +351,13 @@
|
||||
|
||||
.card {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.branch-card__files {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-notifications {
|
||||
|
@ -31,7 +31,7 @@
|
||||
role="listbox"
|
||||
tabindex="-1"
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (e.key == 'Escape') {
|
||||
unselectAllFiles();
|
||||
}
|
||||
}}
|
||||
@ -53,7 +53,6 @@
|
||||
.branch-files {
|
||||
flex: 1;
|
||||
background: var(--clr-bg-1);
|
||||
border-radius: var(--radius-m) var(--radius-m) 0 0;
|
||||
padding: 0 var(--size-14) var(--size-14);
|
||||
/* padding: 0 var(--size-14) var(--size-14); */
|
||||
}
|
||||
</style>
|
||||
|
@ -6,6 +6,7 @@
|
||||
import type { AnyFile } from '$lib/vbranches/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let title: string;
|
||||
export let files: AnyFile[];
|
||||
export let showCheckboxes = false;
|
||||
|
||||
@ -58,7 +59,7 @@
|
||||
/>
|
||||
{/if}
|
||||
<div class="header__title text-base-13 text-semibold">
|
||||
<span>Changes</span>
|
||||
<span>{title}</span>
|
||||
<Badge count={files.length} />
|
||||
</div>
|
||||
</div>
|
||||
@ -69,8 +70,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: var(--size-14);
|
||||
padding-bottom: var(--size-14);
|
||||
padding: var(--size-14);
|
||||
}
|
||||
.header__title {
|
||||
display: flex;
|
||||
|
@ -9,6 +9,7 @@
|
||||
import { sortLikeFileTree } from '$lib/vbranches/filetree';
|
||||
import type { AnyFile } from '$lib/vbranches/types';
|
||||
|
||||
export let title: string = 'Changes';
|
||||
export let files: AnyFile[];
|
||||
export let isUnapplied = false;
|
||||
export let showCheckboxes = false;
|
||||
@ -47,7 +48,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<BranchFilesHeader {files} {showCheckboxes} />
|
||||
<BranchFilesHeader {title} {files} {showCheckboxes} />
|
||||
{#each displayedFiles as file (file.id)}
|
||||
<FileListItem
|
||||
{file}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import PassphraseBox from './PassphraseBox.svelte';
|
||||
import PushButton, { BranchAction } from './PushButton.svelte';
|
||||
import emptyStateImg from '$lib/assets/empty-state/commits-up-to-date.svg?raw';
|
||||
import { PromptService } from '$lib/backend/prompt';
|
||||
import { getContext, getContextStore } from '$lib/utils/context';
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
@ -56,7 +57,15 @@
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
Branch {$branch.name} is up to date with the remote.
|
||||
<div class="empty-state">
|
||||
<span class="text-base-body-12 empty-state__text"
|
||||
>Your branch is up to date with the remote.</span
|
||||
>
|
||||
|
||||
<i class="empty-state__image">
|
||||
{@html emptyStateImg}
|
||||
</i>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@ -65,7 +74,24 @@
|
||||
.actions {
|
||||
background: var(--clr-bg-1);
|
||||
padding: var(--size-16);
|
||||
border-radius: 0 0 var(--radius-m) var(--radius-m);
|
||||
border: 1px solid var(--clr-border-2);
|
||||
}
|
||||
|
||||
/* EMPTY STATE */
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
/* justify-content: space-between; */
|
||||
align-items: center;
|
||||
gap: var(--size-20);
|
||||
}
|
||||
|
||||
.empty-state__image {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.empty-state__text {
|
||||
color: var(--clr-text-3);
|
||||
flex: 1;
|
||||
/* max-width: 8rem; */
|
||||
}
|
||||
</style>
|
||||
|
@ -45,7 +45,6 @@
|
||||
by {branch.author?.name ?? 'unknown'}
|
||||
{/if}
|
||||
</span>
|
||||
<!-- <AuthorIcons authors={branch.authors} /> -->
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -102,7 +102,7 @@
|
||||
<Resizer
|
||||
viewport={rsViewport}
|
||||
direction="right"
|
||||
minWidth={320}
|
||||
minWidth={400}
|
||||
defaultLineColor="var(--clr-border-2)"
|
||||
on:width={(e) => {
|
||||
fileWidth = e.detail / (16 * $userSettings.zoom);
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import BranchFilesList from './BranchFilesList.svelte';
|
||||
import CommitDragItem from './CommitDragItem.svelte';
|
||||
import Icon from './Icon.svelte';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
@ -11,6 +12,7 @@
|
||||
import { draggable } from '$lib/dragging/draggable';
|
||||
import { DraggableCommit, nonDraggable } from '$lib/dragging/draggables';
|
||||
import { getContext, getContextStore } from '$lib/utils/context';
|
||||
import { getTimeAgo } from '$lib/utils/timeAgo';
|
||||
import { openExternalUrl } from '$lib/utils/url';
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
import { createCommitStore, getSelectedFiles } from '$lib/vbranches/contexts';
|
||||
@ -24,7 +26,8 @@
|
||||
BaseBranch,
|
||||
type CommitStatus
|
||||
} from '$lib/vbranches/types';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
// import { slide } from 'svelte/transition';
|
||||
|
||||
export let branch: Branch | undefined = undefined;
|
||||
export let commit: Commit | RemoteCommit;
|
||||
@ -47,8 +50,10 @@
|
||||
|
||||
const currentCommitMessage = persistedCommitMessage(project.id, branch?.id || '');
|
||||
|
||||
let showFiles = false;
|
||||
const dispatch = createEventDispatcher<{ toggle: void }>();
|
||||
|
||||
let files: RemoteFile[] = [];
|
||||
let showDetails = false;
|
||||
|
||||
$: selectedFile =
|
||||
$fileIdSelection.length == 1 &&
|
||||
@ -61,9 +66,10 @@
|
||||
}
|
||||
|
||||
function toggleFiles() {
|
||||
showFiles = !showFiles;
|
||||
showDetails = !showDetails;
|
||||
dispatch('toggle');
|
||||
|
||||
if (showFiles) loadFiles();
|
||||
if (showDetails) loadFiles();
|
||||
}
|
||||
|
||||
function onKeyup(e: KeyboardEvent) {
|
||||
@ -143,176 +149,181 @@
|
||||
</Modal>
|
||||
|
||||
<div
|
||||
use:draggable={commit instanceof Commit
|
||||
? {
|
||||
data: new DraggableCommit(commit.branchId, commit, isHeadCommit)
|
||||
}
|
||||
: nonDraggable()}
|
||||
class="commit"
|
||||
class:is-commit-open={showFiles}
|
||||
class="commit-row"
|
||||
class:is-commit-open={showDetails}
|
||||
class:is-first={first}
|
||||
class:is-last={last}
|
||||
>
|
||||
<div
|
||||
class="accent"
|
||||
class:is-first={first}
|
||||
class:is-last={last}
|
||||
class:local={type == 'local'}
|
||||
class:remote={type == 'remote'}
|
||||
class:upstream={type == 'upstream'}
|
||||
></div>
|
||||
<slot name="lines" />
|
||||
<CommitDragItem {commit}>
|
||||
<div
|
||||
use:draggable={commit instanceof Commit
|
||||
? {
|
||||
data: new DraggableCommit(commit.branchId, commit, isHeadCommit)
|
||||
}
|
||||
: nonDraggable()}
|
||||
class="commit-card"
|
||||
class:is-first={first}
|
||||
class:is-last={last}
|
||||
>
|
||||
<div
|
||||
class="accent-border-line"
|
||||
class:is-first={first}
|
||||
class:is-last={last}
|
||||
class:local={type == 'local'}
|
||||
class:remote={type == 'remote'}
|
||||
class:upstream={type == 'upstream'}
|
||||
/>
|
||||
|
||||
<div class="commit__header" on:click={toggleFiles} on:keyup={onKeyup} role="button" tabindex="0">
|
||||
{#if first}
|
||||
<div class="commit__type text-semibold text-base-12">
|
||||
{#if type == 'remote'}
|
||||
Local and remote <Icon name="local-remote" />
|
||||
{:else if type == 'local'}
|
||||
Local <Icon name="local" />
|
||||
{:else if type == 'upstream'}
|
||||
Remote upstream <Icon name="remote" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="commit__message">
|
||||
{#if $advancedCommitOperations}
|
||||
<div class="commit__id">
|
||||
<code>
|
||||
{#if commit.isSigned}
|
||||
<span class="text-xs">🔒</span>
|
||||
{/if}
|
||||
{#if commit.changeId}
|
||||
{commit.changeId.split('-')[0]}
|
||||
{:else}
|
||||
{commit.id.substring(0, 6)}
|
||||
{/if}
|
||||
</code>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="commit__row">
|
||||
{#if isUndoable}
|
||||
{#if commit.descriptionTitle}
|
||||
<span class="commit__title text-semibold text-base-12" class:truncate={!showFiles}>
|
||||
{commit.descriptionTitle}
|
||||
</span>
|
||||
<div class="commit__content">
|
||||
<!-- GENERAL INFO -->
|
||||
<div
|
||||
class="commit__about"
|
||||
on:click={toggleFiles}
|
||||
on:keyup={onKeyup}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
{#if first}
|
||||
<div class="commit__type text-semibold text-base-12">
|
||||
{#if type == 'remote'}
|
||||
Local and remote
|
||||
{:else if type == 'local'}
|
||||
Local <Icon name="local" />
|
||||
{:else if type == 'upstream'}
|
||||
Remote upstream <Icon name="remote" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isUndoable && !commit.descriptionTitle}
|
||||
<span class="text-base-body-13 text-semibold commit__empty-title"
|
||||
>empty commit message</span
|
||||
>
|
||||
{:else}
|
||||
<span
|
||||
class="commit__title_no_desc text-base-12 text-zinc-400"
|
||||
class:truncate={!showFiles}
|
||||
>
|
||||
<i>empty commit message</i>
|
||||
</span>
|
||||
<h5 class="text-base-body-13 text-semibold commit__title" class:truncate={!showDetails}>
|
||||
{commit.descriptionTitle}
|
||||
</h5>
|
||||
|
||||
{#if $advancedCommitOperations}
|
||||
<div class="text-base-11 commit__subtitle">
|
||||
<span class="commit__id">
|
||||
{#if commit.changeId}
|
||||
{commit.changeId.split('-')[0]}
|
||||
{:else}
|
||||
{commit.id.substring(0, 6)}
|
||||
{/if}
|
||||
|
||||
{#if commit.isSigned}
|
||||
<Icon name="locked-small" />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span class="commit__subtitle-divider">•</span>
|
||||
|
||||
<span
|
||||
>{getTimeAgo(commit.createdAt)}{type == 'remote' || type == 'upstream'
|
||||
? ` by ${commit.author.name}`
|
||||
: ''}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !showFiles}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="undo-small"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
currentCommitMessage.set(commit.description);
|
||||
e.stopPropagation();
|
||||
undoCommit(commit);
|
||||
}}>Undo</Tag
|
||||
>
|
||||
{/if}
|
||||
{:else}
|
||||
<span class="commit__title text-base-12" class:truncate={!showFiles}>
|
||||
{commit.descriptionTitle}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showFiles}
|
||||
{#if commit.descriptionBody}
|
||||
<div class="commit__row" transition:slide={{ duration: 100 }}>
|
||||
<span class="commit__body text-base-body-12">
|
||||
{commit.descriptionBody}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- HIDDEN -->
|
||||
{#if showDetails}
|
||||
<div class="commit__details">
|
||||
{#if hasCommitUrl || isUndoable}
|
||||
<div class="commit__actions hide-native-scrollbar">
|
||||
{#if isUndoable}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="undo-small"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
currentCommitMessage.set(commit.description);
|
||||
e.stopPropagation();
|
||||
undoCommit(commit);
|
||||
}}>Undo</Tag
|
||||
>
|
||||
{#if $advancedCommitOperations}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="edit-text"
|
||||
clickable
|
||||
on:click={openCommitMessageModal}>Edit message</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
reorderCommit(commit, -1);
|
||||
}}>Move Up</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
reorderCommit(commit, 1);
|
||||
}}>Move Down</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
insertBlankCommit(commit, -1);
|
||||
}}>Add Before</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
insertBlankCommit(commit, 1);
|
||||
}}>Add After</Tag
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if hasCommitUrl}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="open-link"
|
||||
clickable
|
||||
on:click={() => {
|
||||
if (commitUrl) openExternalUrl(commitUrl);
|
||||
}}>Open</Tag
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if commit.descriptionBody}
|
||||
<span class="commit__description text-base-body-12">
|
||||
{commit.descriptionBody}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $advancedCommitOperations && isUndoable}
|
||||
<Tag clickable on:click={openCommitMessageModal}>Edit</Tag>
|
||||
{/if}
|
||||
{#if showDetails}
|
||||
<div class="files-container">
|
||||
<BranchFilesList title="Files" {files} {isUnapplied} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- <span class="commit__time text-base-11">
|
||||
<TimeAgo date={commit.createdAt} />
|
||||
</span> -->
|
||||
</div>
|
||||
|
||||
{#if showFiles}
|
||||
<div class="files-container" transition:slide={{ duration: 100 }}>
|
||||
<BranchFilesList {files} {isUnapplied} />
|
||||
</div>
|
||||
|
||||
{#if hasCommitUrl || isUndoable}
|
||||
<div class="files__footer">
|
||||
{#if isUndoable}
|
||||
{#if $advancedCommitOperations}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
reorderCommit(commit, -1);
|
||||
}}>Move Up</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
reorderCommit(commit, 1);
|
||||
}}>Move Down</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
insertBlankCommit(commit, -1);
|
||||
}}>Add Before</Tag
|
||||
>
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
insertBlankCommit(commit, 1);
|
||||
}}>Add After</Tag
|
||||
>
|
||||
{/if}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="undo-small"
|
||||
clickable
|
||||
on:click={(e) => {
|
||||
currentCommitMessage.set(commit.description);
|
||||
e.stopPropagation();
|
||||
undoCommit(commit);
|
||||
}}>Undo</Tag
|
||||
>
|
||||
{/if}
|
||||
{#if hasCommitUrl}
|
||||
<Tag
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
icon="open-link"
|
||||
clickable
|
||||
on:click={() => {
|
||||
if (commitUrl) openExternalUrl(commitUrl);
|
||||
}}>Open commit</Tag
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</CommitDragItem>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
@ -324,33 +335,50 @@
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.commit {
|
||||
.commit-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: var(--size-8);
|
||||
padding-right: var(--size-14);
|
||||
/* border-top: 1px solid var(--clr-border-2); */
|
||||
/* padding-left: var(--size-8); */
|
||||
|
||||
&:not(.is-first) {
|
||||
border-top: 1px solid var(--clr-border-3);
|
||||
}
|
||||
}
|
||||
|
||||
.commit-card {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: var(--clr-bg-1);
|
||||
border: 1px solid var(--clr-border-2);
|
||||
/* border: 1px solid var(--clr-border-2);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
border-left: none; */
|
||||
border-right: 1px solid var(--clr-border-2);
|
||||
overflow: hidden;
|
||||
transition: background-color var(--transition-fast);
|
||||
|
||||
&.is-first {
|
||||
margin-top: var(--size-12);
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
border-top-left-radius: var(--radius-m);
|
||||
border-top-right-radius: var(--radius-m);
|
||||
}
|
||||
&.is-last {
|
||||
border-bottom: 1px solid var(--clr-border-2);
|
||||
border-bottom-left-radius: var(--radius-m);
|
||||
border-bottom-right-radius: var(--radius-m);
|
||||
}
|
||||
&:not(.is-first) {
|
||||
border-top: none;
|
||||
}
|
||||
&:not(.is-commit-open):hover {
|
||||
background-color: var(--clr-bg-2);
|
||||
}
|
||||
}
|
||||
|
||||
.accent {
|
||||
.accent-border-line {
|
||||
position: absolute;
|
||||
width: var(--size-4);
|
||||
height: 100%;
|
||||
@ -363,99 +391,114 @@
|
||||
&.upstream {
|
||||
background-color: var(--clr-commit-upstream);
|
||||
}
|
||||
&.is-first {
|
||||
border-top-left-radius: var(--radius-m);
|
||||
}
|
||||
&.is-last {
|
||||
border-bottom-left-radius: var(--radius-m);
|
||||
}
|
||||
}
|
||||
|
||||
.commit__header {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-10);
|
||||
padding: var(--size-14);
|
||||
}
|
||||
|
||||
.commit__type {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.is-commit-open {
|
||||
background-color: var(--clr-bg-2);
|
||||
|
||||
& .commit__header {
|
||||
padding-bottom: var(--size-16);
|
||||
border-bottom: 1px solid var(--clr-border-2);
|
||||
}
|
||||
|
||||
& .commit__message {
|
||||
margin-bottom: var(--size-4);
|
||||
}
|
||||
/* HEADER */
|
||||
.commit__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.commit__message {
|
||||
.commit__about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-6);
|
||||
padding: var(--size-14);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--clr-bg-1-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.commit__title {
|
||||
flex: 1;
|
||||
display: block;
|
||||
color: var(--clr-scale-ntrl-0);
|
||||
width: 100%;
|
||||
}
|
||||
.commit__title_no_desc {
|
||||
flex: 1;
|
||||
display: block;
|
||||
color: var(--clr-text-1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.commit__body {
|
||||
flex: 1;
|
||||
display: block;
|
||||
width: 100%;
|
||||
color: var(--clr-scale-ntrl-40);
|
||||
white-space: pre-line;
|
||||
word-wrap: anywhere;
|
||||
.commit__description {
|
||||
color: var(--clr-text-2);
|
||||
}
|
||||
|
||||
.commit__row {
|
||||
.commit__empty-title {
|
||||
color: var(--clr-text-3);
|
||||
}
|
||||
|
||||
.commit__subtitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--size-8);
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--size-4);
|
||||
color: var(--clr-text-2);
|
||||
overflow: hidden;
|
||||
|
||||
& > span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.commit__id {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -14px;
|
||||
}
|
||||
.commit__id > code {
|
||||
background-color: #eeeeee;
|
||||
padding: 1px 12px;
|
||||
color: #888888;
|
||||
font-size: x-small;
|
||||
border-radius: 0px 0px 6px 6px;
|
||||
margin-bottom: -8px;
|
||||
gap: var(--size-4);
|
||||
}
|
||||
|
||||
.files-container {
|
||||
background-color: var(--clr-bg-1);
|
||||
padding: 0 var(--size-14) var(--size-14);
|
||||
.commit__subtitle-divider {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.files__footer {
|
||||
/* DETAILS */
|
||||
|
||||
.commit__details {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--size-8);
|
||||
flex-direction: column;
|
||||
gap: var(--size-12);
|
||||
padding: var(--size-14);
|
||||
background-color: var(--clr-bg-1);
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
}
|
||||
|
||||
.commit__actions {
|
||||
display: flex;
|
||||
gap: var(--size-4);
|
||||
overflow-x: auto;
|
||||
margin: 0 calc(var(--size-14) * -1);
|
||||
padding: 0 var(--size-14);
|
||||
}
|
||||
|
||||
/* FILES */
|
||||
.files-container {
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
}
|
||||
|
||||
/* MODIFIERS */
|
||||
.is-commit-open {
|
||||
&:not(.is-first) {
|
||||
border-top: 1px solid var(--clr-border-3);
|
||||
|
||||
& .commit-card {
|
||||
margin-top: var(--size-8);
|
||||
}
|
||||
}
|
||||
|
||||
& .commit-card {
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
border-bottom: 1px solid var(--clr-border-2);
|
||||
border-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
&:not(.is-last) .commit-card {
|
||||
margin-bottom: var(--size-8);
|
||||
}
|
||||
|
||||
& .commit__about {
|
||||
background-color: var(--clr-bg-1-muted);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -92,7 +92,6 @@
|
||||
background: var(--clr-bg-1);
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
transition: background-color var(--transition-medium);
|
||||
border-radius: 0 0 var(--radius-m) var(--radius-m);
|
||||
}
|
||||
|
||||
.commit-box__expander {
|
||||
|
@ -17,7 +17,7 @@
|
||||
export let base = false;
|
||||
</script>
|
||||
|
||||
<div class="lines" class:base>
|
||||
<div class="lines">
|
||||
{#if hasShadowColumn}
|
||||
<ShadowLine
|
||||
line={shadowLine}
|
||||
@ -42,8 +42,10 @@
|
||||
(!!remoteCommit && !remoteCommit?.children?.[0])}
|
||||
{base}
|
||||
/>
|
||||
|
||||
{#if hasLocalColumn}
|
||||
<LocalLine
|
||||
isEmpty={base}
|
||||
commit={localCommit?.status == 'local' ? localCommit : undefined}
|
||||
dashed={localLine}
|
||||
{first}
|
||||
@ -55,9 +57,8 @@
|
||||
.lines {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
min-height: var(--size-16);
|
||||
&.base {
|
||||
height: var(--size-40);
|
||||
}
|
||||
min-height: var(--size-12);
|
||||
padding-left: var(--size-8);
|
||||
/* margin-top: -1px; */
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
import CommitCard from './CommitCard.svelte';
|
||||
import CommitLines from './CommitLines.svelte';
|
||||
import CommitListItem from './CommitListItem.svelte';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { getContext } from '$lib/utils/context';
|
||||
import { getContextStore } from '$lib/utils/context';
|
||||
import { getLocalCommits, getRemoteCommits, getUnknownCommits } from '$lib/vbranches/contexts';
|
||||
import { BaseBranch, Branch } from '$lib/vbranches/types';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let isUnapplied: boolean;
|
||||
|
||||
@ -13,22 +15,37 @@
|
||||
const remoteCommits = getRemoteCommits();
|
||||
const unknownCommits = getUnknownCommits();
|
||||
const baseBranch = getContextStore(BaseBranch);
|
||||
const project = getContext(Project);
|
||||
|
||||
$: hasShadowColumn = $localCommits.some((c) => c.relatedTo && c.id != c.relatedTo.id);
|
||||
$: hasLocalColumn = $localCommits.length > 0;
|
||||
$: hasCommits = $branch.commits && $branch.commits.length > 0;
|
||||
$: headCommit = $branch.commits.at(0);
|
||||
$: hasUnknownCommits = $unknownCommits.length > 0;
|
||||
$: baseCommit = $baseBranch.recentCommits.at($baseBranch.recentCommits.length - 1)?.id;
|
||||
|
||||
let baseIsUnfolded = false;
|
||||
</script>
|
||||
|
||||
{#if hasCommits || hasUnknownCommits}
|
||||
<div class="commit-list__content">
|
||||
<div class="title text-base-13 text-semibold"></div>
|
||||
<div class="commits">
|
||||
{#if $unknownCommits.length > 0}
|
||||
<CommitLines {hasShadowColumn} {hasLocalColumn} localLine />
|
||||
{#each $unknownCommits as commit, idx (commit.id)}
|
||||
<div class="commit-lines">
|
||||
<div class="commits">
|
||||
<!-- UPSTREAM COMMITS -->
|
||||
{#if $unknownCommits.length > 0}
|
||||
{#each $unknownCommits as commit, idx (commit.id)}
|
||||
<CommitCard
|
||||
type="upstream"
|
||||
branch={$branch}
|
||||
{commit}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $unknownCommits.length - 1}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
on:toggle={() => {
|
||||
console.log('toggle upstream');
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="lines">
|
||||
<CommitLines
|
||||
{hasLocalColumn}
|
||||
{hasShadowColumn}
|
||||
@ -37,30 +54,27 @@
|
||||
remoteCommit={commit}
|
||||
first={idx == 0}
|
||||
/>
|
||||
<CommitListItem {commit}>
|
||||
<CommitCard
|
||||
type="upstream"
|
||||
branch={$branch}
|
||||
{commit}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $unknownCommits.length - 1}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
/>
|
||||
</CommitListItem>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if $localCommits.length > 0}
|
||||
<CommitLines
|
||||
{hasShadowColumn}
|
||||
{hasLocalColumn}
|
||||
upstreamLine={hasUnknownCommits}
|
||||
localLine
|
||||
/>
|
||||
{#each $localCommits as commit, idx (commit.id)}
|
||||
<div class="commit-lines">
|
||||
</svelte:fragment>
|
||||
</CommitCard>
|
||||
{/each}
|
||||
{/if}
|
||||
<!-- LOCAL COMMITS -->
|
||||
{#if $localCommits.length > 0}
|
||||
{#each $localCommits as commit, idx (commit.id)}
|
||||
<CommitCard
|
||||
branch={$branch}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $localCommits.length - 1}
|
||||
type="local"
|
||||
on:toggle={() => {
|
||||
console.log('toggle local');
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="lines">
|
||||
<CommitLines
|
||||
{hasLocalColumn}
|
||||
{hasShadowColumn}
|
||||
@ -69,30 +83,28 @@
|
||||
first={idx == 0}
|
||||
upstreamLine={hasUnknownCommits}
|
||||
/>
|
||||
<CommitListItem {commit}>
|
||||
<CommitCard
|
||||
branch={$branch}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $localCommits.length - 1}
|
||||
type="local"
|
||||
/>
|
||||
</CommitListItem>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if $remoteCommits.length > 0}
|
||||
<CommitLines
|
||||
{hasShadowColumn}
|
||||
{hasLocalColumn}
|
||||
upstreamLine={hasUnknownCommits}
|
||||
localLine
|
||||
/>
|
||||
{#each $remoteCommits as commit, idx (commit.id)}
|
||||
<div class="commit-lines">
|
||||
</svelte:fragment>
|
||||
</CommitCard>
|
||||
<!-- </div> -->
|
||||
{/each}
|
||||
{/if}
|
||||
<!-- REMOTE COMMITS -->
|
||||
{#if $remoteCommits.length > 0}
|
||||
{#each $remoteCommits as commit, idx (commit.id)}
|
||||
<CommitCard
|
||||
branch={$branch}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $remoteCommits.length - 1}
|
||||
type="remote"
|
||||
on:toggle={() => {
|
||||
console.log('toggle remote');
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="lines">
|
||||
<CommitLines
|
||||
{hasLocalColumn}
|
||||
{hasShadowColumn}
|
||||
@ -101,45 +113,118 @@
|
||||
first={idx == 0}
|
||||
upstreamLine={hasUnknownCommits}
|
||||
/>
|
||||
<CommitListItem {commit}>
|
||||
<CommitCard
|
||||
branch={$branch}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
isHeadCommit={commit.id === headCommit?.id}
|
||||
{isUnapplied}
|
||||
first={idx == 0}
|
||||
last={idx == $remoteCommits.length - 1}
|
||||
type="remote"
|
||||
/>
|
||||
</CommitListItem>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<CommitLines
|
||||
{hasShadowColumn}
|
||||
localLine={$remoteCommits.length == 0 && $localCommits.length > 0}
|
||||
localRoot={$remoteCommits.length == 0 && $localCommits.length > 0}
|
||||
remoteLine={$remoteCommits.length > 0}
|
||||
shadowLine={hasShadowColumn}
|
||||
base
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</CommitCard>
|
||||
{/each}
|
||||
{/if}
|
||||
<!-- BASE -->
|
||||
<div class="base-row-container" class:base-row-container_unfolded={baseIsUnfolded}>
|
||||
<div
|
||||
class="commit-group base-row"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
on:click|stopPropagation={() => (baseIsUnfolded = !baseIsUnfolded)}
|
||||
on:keydown={(e) => e.key === 'Enter' && (baseIsUnfolded = !baseIsUnfolded)}
|
||||
>
|
||||
<div class="base-row__lines">
|
||||
<CommitLines
|
||||
{hasShadowColumn}
|
||||
localLine={$remoteCommits.length == 0 && $localCommits.length > 0}
|
||||
localRoot={$remoteCommits.length == 0 && $localCommits.length > 0}
|
||||
remoteLine={$remoteCommits.length > 0}
|
||||
shadowLine={hasShadowColumn}
|
||||
{hasLocalColumn}
|
||||
base
|
||||
/>
|
||||
</div>
|
||||
<div class="base-row__content">
|
||||
<span class="text-base-11 base-row__text"
|
||||
>Base commit <button
|
||||
class="base-row__commit-link"
|
||||
on:click={async () => await goto(`/${project.id}/base`)}
|
||||
>
|
||||
{baseCommit ? baseCommit.slice(0, 7) : ''}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.commit-lines {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.commit-list__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.commits {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--clr-bg-2);
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
border-bottom: 1px solid var(--clr-border-2);
|
||||
|
||||
--base-top-margin: var(--size-8);
|
||||
--base-icon-top: var(--size-16);
|
||||
--base-unfolded: var(--size-48);
|
||||
|
||||
--avatar-first-top: 3.1rem;
|
||||
--avatar-top: var(--size-16);
|
||||
}
|
||||
|
||||
.commit-group {
|
||||
/* padding-right: var(--size-14);
|
||||
padding-left: var(--size-8); */
|
||||
}
|
||||
|
||||
/* BASE ROW */
|
||||
|
||||
.base-row-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--size-20);
|
||||
|
||||
overflow: hidden;
|
||||
transition: height var(--transition-medium);
|
||||
}
|
||||
|
||||
.base-row-container_unfolded {
|
||||
height: var(--base-unfolded);
|
||||
--base-icon-top: var(--size-20);
|
||||
|
||||
& .base-row__text {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.base-row {
|
||||
display: flex;
|
||||
gap: var(--size-8);
|
||||
border-top: 1px solid var(--clr-border-3);
|
||||
min-height: calc(var(--base-unfolded) - var(--base-top-margin));
|
||||
margin-top: var(--base-top-margin);
|
||||
transition: background-color var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--clr-bg-2-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.base-row__lines {
|
||||
display: flex;
|
||||
margin-top: calc(var(--size-8) * -1);
|
||||
}
|
||||
|
||||
.base-row__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.base-row__text {
|
||||
color: var(--clr-text-2);
|
||||
opacity: 0;
|
||||
margin-top: var(--size-2);
|
||||
transition: opacity var(--transition-medium);
|
||||
}
|
||||
|
||||
.base-row__commit-link {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -59,7 +59,60 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class:list-item-wrapper={showCheckbox}>
|
||||
<div
|
||||
bind:this={draggableElt}
|
||||
class="file-list-item"
|
||||
class:selected-draggable={selected}
|
||||
id={`file-${file.id}`}
|
||||
data-locked={file.locked}
|
||||
on:click
|
||||
on:keydown
|
||||
on:dragstart={() => {
|
||||
// Reset selection if the file being dragged is not in the selected list
|
||||
if ($fileIdSelection.length > 0 && !fileIdSelection.has(file.id, $commit?.id)) {
|
||||
fileIdSelection.clear();
|
||||
fileIdSelection.add(file.id, $commit?.id);
|
||||
}
|
||||
|
||||
if ($selectedFiles.length > 0) {
|
||||
$selectedFiles.forEach((f) => {
|
||||
if (f.locked) {
|
||||
const lockedElement = document.getElementById(`file-${f.id}`);
|
||||
|
||||
if (lockedElement) {
|
||||
// add a class to the locked file
|
||||
lockedElement.classList.add('locked-file-animation');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (file.locked) {
|
||||
draggableElt.classList.add('locked-file-animation');
|
||||
}
|
||||
}}
|
||||
on:animationend={() => {
|
||||
// remove the class after the animation ends
|
||||
if (file.locked) {
|
||||
draggableElt.classList.remove('locked-file-animation');
|
||||
}
|
||||
}}
|
||||
use:draggable={{
|
||||
data: new DraggableFile($branch?.id || '', file, $commit, selectedFiles),
|
||||
disabled: readonly || isUnapplied,
|
||||
viewportId: 'board-viewport',
|
||||
selector: '.selected-draggable'
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:contextmenu|preventDefault={(e) => {
|
||||
const files = fileIdSelection.has(file.id, $commit?.id)
|
||||
? $fileIdSelection
|
||||
.map((key) => $selectedFiles?.find((f) => fileKey(f.id, $commit?.id) == key))
|
||||
.filter(isDefined)
|
||||
: [file];
|
||||
if (files.length > 0) popupMenu.openByMouse(e, { files });
|
||||
else console.error('No files selected');
|
||||
}}
|
||||
>
|
||||
{#if showCheckbox}
|
||||
<Checkbox
|
||||
small
|
||||
@ -74,97 +127,39 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<div
|
||||
bind:this={draggableElt}
|
||||
class="file-list-item"
|
||||
class:selected-draggable={selected}
|
||||
id={`file-${file.id}`}
|
||||
data-locked={file.locked}
|
||||
on:click
|
||||
on:keydown
|
||||
on:dragstart={() => {
|
||||
// Reset selection if the file being dragged is not in the selected list
|
||||
if ($fileIdSelection.length > 0 && !fileIdSelection.has(file.id, $commit?.id)) {
|
||||
fileIdSelection.clear();
|
||||
fileIdSelection.add(file.id, $commit?.id);
|
||||
}
|
||||
|
||||
if ($selectedFiles.length > 0) {
|
||||
$selectedFiles.forEach((f) => {
|
||||
if (f.locked) {
|
||||
const lockedElement = document.getElementById(`file-${f.id}`);
|
||||
|
||||
if (lockedElement) {
|
||||
// add a class to the locked file
|
||||
lockedElement.classList.add('locked-file-animation');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (file.locked) {
|
||||
draggableElt.classList.add('locked-file-animation');
|
||||
}
|
||||
}}
|
||||
on:animationend={() => {
|
||||
// remove the class after the animation ends
|
||||
if (file.locked) {
|
||||
draggableElt.classList.remove('locked-file-animation');
|
||||
}
|
||||
}}
|
||||
use:draggable={{
|
||||
data: new DraggableFile($branch?.id || '', file, $commit, selectedFiles),
|
||||
disabled: readonly || isUnapplied,
|
||||
viewportId: 'board-viewport',
|
||||
selector: '.selected-draggable'
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:contextmenu|preventDefault={(e) => {
|
||||
const files = fileIdSelection.has(file.id, $commit?.id)
|
||||
? $fileIdSelection
|
||||
.map((key) => $selectedFiles?.find((f) => fileKey(f.id, $commit?.id) == key))
|
||||
.filter(isDefined)
|
||||
: [file];
|
||||
if (files.length > 0) popupMenu.openByMouse(e, { files });
|
||||
else console.error('No files selected');
|
||||
}}
|
||||
>
|
||||
<div class="info">
|
||||
<img draggable="false" class="file-icon" src={getVSIFileIcon(file.path)} alt="" />
|
||||
<span class="text-base-12 name">
|
||||
{file.filename}
|
||||
</span>
|
||||
<span class="text-base-12 path">
|
||||
{file.justpath}
|
||||
</span>
|
||||
</div>
|
||||
<FileStatusIcons {file} />
|
||||
<div class="info">
|
||||
<img draggable="false" class="file-icon" src={getVSIFileIcon(file.path)} alt="" />
|
||||
<span class="text-base-12 name">
|
||||
{file.filename}
|
||||
</span>
|
||||
<span class="text-base-12 path">
|
||||
{file.justpath}
|
||||
</span>
|
||||
</div>
|
||||
<FileStatusIcons {file} />
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.list-item-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--size-8);
|
||||
}
|
||||
|
||||
.file-list-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--size-28);
|
||||
padding: var(--size-4) var(--size-8);
|
||||
gap: var(--size-16);
|
||||
border-radius: var(--radius-s);
|
||||
padding: var(--size-6) var(--size-14);
|
||||
gap: var(--size-10);
|
||||
height: 2rem;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
background: var(--clr-bg-1);
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid var(--clr-border-3);
|
||||
|
||||
&:not(.selected-draggable):hover {
|
||||
background-color: var(--clr-bg-2);
|
||||
background-color: var(--clr-bg-1-muted);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,6 +175,7 @@
|
||||
.file-icon {
|
||||
width: var(--size-12);
|
||||
}
|
||||
|
||||
.name {
|
||||
color: var(--clr-scale-ntrl-0);
|
||||
white-space: nowrap;
|
||||
@ -188,6 +184,7 @@
|
||||
overflow: hidden;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.path {
|
||||
color: var(--clr-scale-ntrl-0);
|
||||
line-height: 120%;
|
||||
@ -198,12 +195,9 @@
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.selected-draggable {
|
||||
background-color: var(--clr-scale-pop-80);
|
||||
border: 1px solid var(--clr-bg-1);
|
||||
/* MODIFIERS */
|
||||
|
||||
&:hover {
|
||||
background-color: var(--clr-scale-pop-80);
|
||||
}
|
||||
.selected-draggable {
|
||||
background-color: var(--clr-theme-pop-bg);
|
||||
}
|
||||
</style>
|
||||
|
@ -6,6 +6,7 @@
|
||||
export let dashed: boolean;
|
||||
export let commit: Commit | undefined;
|
||||
export let first: boolean;
|
||||
export let isEmpty: boolean = false;
|
||||
|
||||
$: hasRoot = isRoot(commit);
|
||||
$: tooltipText = getAvatarTooltip(commit);
|
||||
@ -16,36 +17,39 @@
|
||||
</script>
|
||||
|
||||
<div class="local-column">
|
||||
{#if !commit && dashed}
|
||||
<div class="local-line dashed"></div>
|
||||
{:else if commit}
|
||||
{#if first}
|
||||
<div class="local-line dashed tip" />
|
||||
{#if !isEmpty}
|
||||
{#if !commit && dashed}
|
||||
<div class="local-line dashed"></div>
|
||||
{:else if commit}
|
||||
{#if first}
|
||||
<div class="local-line dashed tip" />
|
||||
{/if}
|
||||
<div class="local-line" class:has-root={hasRoot} class:short={first} />
|
||||
{/if}
|
||||
{#if commit}
|
||||
{@const author = commit.author}
|
||||
<div class="avatar" class:first>
|
||||
<Avatar {author} status={commit.status} help={tooltipText} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if hasRoot}
|
||||
<div class="root" class:long-root={commit?.parent} />
|
||||
{/if}
|
||||
<div class="local-line" class:has-root={hasRoot} class:short={first} />
|
||||
{/if}
|
||||
{#if commit}
|
||||
{@const author = commit.author}
|
||||
<div class="avatar" class:first>
|
||||
<Avatar {author} status={commit.status} help={tooltipText} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if hasRoot}
|
||||
<div class="root" class:long-root={commit?.parent} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.local-column {
|
||||
position: relative;
|
||||
width: var(--size-16);
|
||||
width: var(--size-14);
|
||||
/* background-color: rgba(255, 228, 196, 0.46); */
|
||||
}
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: var(--size-12);
|
||||
left: calc(-1 * var(--size-4));
|
||||
top: var(--avatar-top);
|
||||
left: -0.188rem;
|
||||
&.first {
|
||||
top: 2.625rem;
|
||||
top: var(--avatar-first-top);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,14 +83,14 @@
|
||||
.root {
|
||||
position: absolute;
|
||||
width: var(--size-10);
|
||||
top: calc(100% - var(--size-12));
|
||||
top: calc(100% - var(--size-14));
|
||||
left: calc(-1 * var(--size-4));
|
||||
bottom: calc(-1 * var(--size-2));
|
||||
border-radius: 0 0 var(--radius-l) 0;
|
||||
border-color: var(--clr-commit-local);
|
||||
border-width: 0 var(--size-2) var(--size-2) 0;
|
||||
&.long-root {
|
||||
bottom: -3rem;
|
||||
bottom: -2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -15,13 +15,13 @@
|
||||
$: tooltipText = getAvatarTooltip(commit || remoteCommit);
|
||||
</script>
|
||||
|
||||
<div class="remote-column" class:has-root={root}>
|
||||
<div class="remote-column" class:has-root={root} class:base>
|
||||
{#if base}
|
||||
<div class="remote-line dashed" class:short={!line} />
|
||||
{#if root}
|
||||
<div class="root base" />
|
||||
{/if}
|
||||
<div class="commit-icon">
|
||||
<div class="base-icon">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
@ -68,6 +68,11 @@
|
||||
.remote-column {
|
||||
position: relative;
|
||||
width: var(--size-24);
|
||||
/* background-color: rgba(125, 138, 154, 0.307); */
|
||||
|
||||
/* &.base {
|
||||
margin-top: calc(var(--size-8) * -1);
|
||||
} */
|
||||
}
|
||||
|
||||
.remote-line {
|
||||
@ -81,9 +86,9 @@
|
||||
top: calc(var(--size-40) + var(--size-2));
|
||||
}
|
||||
&.short {
|
||||
top: 1rem;
|
||||
top: var(--avatar-top);
|
||||
&.first {
|
||||
top: 3rem;
|
||||
top: var(--avatar-first-top);
|
||||
}
|
||||
}
|
||||
&.tip {
|
||||
@ -102,9 +107,9 @@
|
||||
background-color: var(--clr-commit-upstream);
|
||||
top: 0;
|
||||
&.short {
|
||||
top: 1rem;
|
||||
top: var(--avatar-top);
|
||||
&.first {
|
||||
top: calc(var(--size-40) + var(--size-2));
|
||||
top: var(--avatar-first-top);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,10 +117,10 @@
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: var(--size-10);
|
||||
top: var(--avatar-top);
|
||||
left: var(--size-4);
|
||||
&.first {
|
||||
top: calc(var(--size-40) + var(--size-2));
|
||||
top: var(--avatar-first-top);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,24 +129,29 @@
|
||||
width: var(--size-10);
|
||||
top: 1.875rem;
|
||||
border-radius: var(--radius-l) 0 0 0;
|
||||
height: var(--size-10);
|
||||
height: var(--size-16);
|
||||
left: calc(var(--size-10) + var(--size-1));
|
||||
border-color: var(--clr-commit-local);
|
||||
border-width: var(--size-2) 0 0 var(--size-2);
|
||||
&.base {
|
||||
top: 0;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.commit-icon {
|
||||
display: inline-block;
|
||||
.base-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
left: var(--size-4);
|
||||
top: var(--base-icon-top);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--clr-commit-remote);
|
||||
height: var(--size-16);
|
||||
width: var(--size-16);
|
||||
top: var(--size-10);
|
||||
height: 1.125rem;
|
||||
width: 1.125rem;
|
||||
transition: top var(--transition-medium);
|
||||
|
||||
& svg {
|
||||
height: var(--size-16);
|
||||
width: var(--size-16);
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Avatar from './Avatar.svelte';
|
||||
import { getAvatarTooltip } from '$lib/utils/avatar';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import type { Commit, RemoteCommit } from '$lib/vbranches/types';
|
||||
@ -27,12 +26,6 @@
|
||||
{#if localCommit}
|
||||
<div class="shadow-marker" class:first class:short use:tooltip={tooltipText}></div>
|
||||
{/if}
|
||||
{#if remoteCommit}
|
||||
{@const author = remoteCommit.author}
|
||||
<div class="avatar" class:first class:short>
|
||||
<Avatar {author} status={remoteCommit.status} help={tooltipText} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
@ -52,9 +45,9 @@
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
&.short {
|
||||
top: 1rem;
|
||||
top: calc(var(--avatar-top) + var(--size-2));
|
||||
&.first {
|
||||
top: 3rem;
|
||||
top: calc(var(--avatar-first-top) + var(--size-2));
|
||||
}
|
||||
}
|
||||
&.dashed {
|
||||
@ -80,19 +73,10 @@
|
||||
height: var(--size-10);
|
||||
border-radius: 100%;
|
||||
background-color: var(--clr-commit-shadow);
|
||||
top: var(--size-14);
|
||||
top: calc(var(--avatar-top) + var(--size-2));
|
||||
left: 50%;
|
||||
&.first {
|
||||
top: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: var(--size-10);
|
||||
left: var(--size-4);
|
||||
&.first {
|
||||
top: calc(var(--size-40) + var(--size-2));
|
||||
top: calc(var(--avatar-first-top) + var(--size-2));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
export let foldable: boolean = false;
|
||||
export let foldedAmount: number | undefined = undefined;
|
||||
export let foldedHeight = '2.6rem';
|
||||
export let foldedHeight = '3rem';
|
||||
|
||||
let isOpen: boolean = false;
|
||||
let el: HTMLElement;
|
||||
|
@ -303,7 +303,7 @@
|
||||
position: absolute;
|
||||
top: var(--size-24);
|
||||
content: '';
|
||||
height: calc(100% - var(--size-12));
|
||||
height: calc(100% - var(--size-14));
|
||||
min-height: var(--size-8);
|
||||
width: 1px;
|
||||
background-color: var(--clr-border-2);
|
||||
@ -356,23 +356,26 @@
|
||||
.files-attacment {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-2);
|
||||
padding: var(--size-4);
|
||||
}
|
||||
|
||||
.files-attacment__file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--size-6);
|
||||
padding: var(--size-4);
|
||||
padding: var(--size-8);
|
||||
border-bottom: 1px solid var(--clr-border-3);
|
||||
|
||||
&:not(.file-selected):hover {
|
||||
background-color: var(--clr-bg-1-muted);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.file-selected {
|
||||
background-color: var(--clr-scale-pop-80);
|
||||
background-color: var(--clr-theme-pop-bg);
|
||||
|
||||
& .files-attacment__file-name {
|
||||
opacity: 0.9;
|
||||
|
@ -16,6 +16,8 @@
|
||||
// Style props
|
||||
export let style: ComponentColor = 'neutral';
|
||||
export let kind: ComponentStyleKind = 'soft';
|
||||
|
||||
const SLOTS = $$props.$$slots;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -30,9 +32,12 @@
|
||||
on:mousedown
|
||||
on:contextmenu
|
||||
>
|
||||
<span class="label">
|
||||
<slot />
|
||||
</span>
|
||||
{#if SLOTS?.default}
|
||||
<span class="label">
|
||||
<slot />
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<Icon name="spinner" />
|
||||
{:else if icon}
|
||||
|
@ -1,35 +1,12 @@
|
||||
import { formatDistanceToNowStrict } from 'date-fns';
|
||||
import { writable, type Readable } from 'svelte/store';
|
||||
|
||||
export function createTimeAgoStore(
|
||||
date: Date | undefined,
|
||||
addSuffix: boolean = false
|
||||
): Readable<string> | undefined {
|
||||
if (!date) return;
|
||||
let timeoutId: number;
|
||||
return writable<string>(formatDistanceToNowStrict(date, { addSuffix }), (set) => {
|
||||
function updateStore() {
|
||||
if (!date) return;
|
||||
const seconds = Math.round(Math.abs((new Date().getTime() - date.getTime()) / 1000.0));
|
||||
const msUntilNextUpdate = Number.isNaN(seconds)
|
||||
? 1000
|
||||
: getSecondsUntilUpdate(seconds) * 1000;
|
||||
if (seconds < 10) {
|
||||
set('just now');
|
||||
} else if (seconds < 60) {
|
||||
set(`< 1 min ${addSuffix ? ' ago' : ''}`);
|
||||
} else {
|
||||
set(customFormatDistance(date, addSuffix));
|
||||
}
|
||||
timeoutId = window.setTimeout(() => {
|
||||
updateStore();
|
||||
}, msUntilNextUpdate);
|
||||
}
|
||||
updateStore();
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
});
|
||||
function customFormatDistance(date: Date, addSuffix: boolean): string {
|
||||
const distance = formatDistanceToNowStrict(date, { addSuffix });
|
||||
return distance.replace(
|
||||
/\b(seconds?|minutes?|hours?|days?|months?|years?)\b/g,
|
||||
(match) => unitShorthandMap[match]
|
||||
);
|
||||
}
|
||||
|
||||
function getSecondsUntilUpdate(seconds: number) {
|
||||
@ -47,14 +24,52 @@ function getSecondsUntilUpdate(seconds: number) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimeAgo(date: Date, addSuffix: boolean = true): string {
|
||||
const seconds = Math.round(Math.abs((new Date().getTime() - date.getTime()) / 1000.0));
|
||||
if (seconds < 10) {
|
||||
return 'just now';
|
||||
} else if (seconds < 60) {
|
||||
return `< 1 min ${addSuffix ? ' ago' : ''}`;
|
||||
} else {
|
||||
return customFormatDistance(date, addSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
export function createTimeAgoStore(
|
||||
date: Date | undefined,
|
||||
addSuffix: boolean = false
|
||||
): Readable<string> | undefined {
|
||||
if (!date) return;
|
||||
let timeoutId: number;
|
||||
return writable<string>(getTimeAgo(date, addSuffix), (set) => {
|
||||
function updateStore() {
|
||||
if (!date) return;
|
||||
const seconds = Math.round(Math.abs((new Date().getTime() - date.getTime()) / 1000.0));
|
||||
const msUntilNextUpdate = Number.isNaN(seconds)
|
||||
? 1000
|
||||
: getSecondsUntilUpdate(seconds) * 1000;
|
||||
|
||||
set(getTimeAgo(date, addSuffix));
|
||||
|
||||
timeoutId = window.setTimeout(() => {
|
||||
updateStore();
|
||||
}, msUntilNextUpdate);
|
||||
}
|
||||
updateStore();
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// SHORTHAND WORDS
|
||||
const unitShorthandMap: Record<string, string> = {
|
||||
second: 'sec',
|
||||
seconds: 'sec',
|
||||
minute: 'min',
|
||||
minutes: 'min',
|
||||
hour: 'hr',
|
||||
hours: 'hr',
|
||||
hour: 'hour',
|
||||
hours: 'hours',
|
||||
day: 'day',
|
||||
days: 'days',
|
||||
month: 'mo',
|
||||
@ -62,11 +77,3 @@ const unitShorthandMap: Record<string, string> = {
|
||||
year: 'yr',
|
||||
years: 'yr'
|
||||
};
|
||||
|
||||
function customFormatDistance(date: Date, addSuffix: boolean): string {
|
||||
const distance = formatDistanceToNowStrict(date, { addSuffix });
|
||||
return distance.replace(
|
||||
/\b(seconds?|minutes?|hours?|days?|months?|years?)\b/g,
|
||||
(match) => unitShorthandMap[match]
|
||||
);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
/* CSS VARIABLES */
|
||||
:root {
|
||||
--transition-fast: 0.06s ease-in-out;
|
||||
--transition-medium: 0.1s ease-in-out;
|
||||
--transition-medium: 0.15s ease-in-out;
|
||||
--transition-slow: 0.2s ease-in-out;
|
||||
|
||||
/* Hover ratio for oklch */
|
||||
|
Loading…
Reference in New Issue
Block a user