mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-22 09:01:45 +03:00
Tooltip-refactoring-+-new-component (#4804)
* tooltip component + custom svelte transitions * update some tooltips * replace old toogle * replace old tooltip hook * remove old tooltip hook * lint fixes * design tokens update
This commit is contained in:
parent
98c3f5d310
commit
c820a33e41
@ -47,7 +47,7 @@
|
||||
icon="pr-small"
|
||||
style="success"
|
||||
kind="solid"
|
||||
help="These changes have been integrated upstream, update your workspace to make this lane disappear."
|
||||
tooltip="Changes have been integrated upstream, update your workspace to make this lane disappear."
|
||||
reversedDirection>Integrated</Button
|
||||
>
|
||||
{:else}
|
||||
@ -56,7 +56,7 @@
|
||||
size="tag"
|
||||
icon="virtual-branch-small"
|
||||
style="neutral"
|
||||
help="These changes are in your working directory."
|
||||
tooltip="Changes are in your working directory"
|
||||
reversedDirection>Virtual</Button
|
||||
>
|
||||
{/if}
|
||||
@ -68,7 +68,7 @@
|
||||
style="neutral"
|
||||
shrinkable
|
||||
disabled
|
||||
help="Branch name that will be used when pushing. You can change it from the lane menu."
|
||||
tooltip={'Branch name that will be used when pushing.\nChange it from the lane menu'}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
@ -81,7 +81,7 @@
|
||||
style="neutral"
|
||||
kind="solid"
|
||||
icon="remote-branch-small"
|
||||
help="At least some of your changes have been pushed"
|
||||
tooltip="Some changes have been pushed"
|
||||
reversedDirection>Remote</Button
|
||||
>
|
||||
<Button
|
||||
|
@ -132,7 +132,7 @@
|
||||
<div class="draggable" data-drag-handle>
|
||||
<Icon name="draggable" />
|
||||
</div>
|
||||
<Button style="ghost" outline icon="unfold-lane" help="Expand lane" onclick={expandLane} />
|
||||
<Button style="ghost" outline icon="unfold-lane" tooltip="Expand lane" onclick={expandLane} />
|
||||
</div>
|
||||
|
||||
<div class="collapsed-lane__info-wrap" bind:clientHeight={headerInfoHeight}>
|
||||
@ -147,7 +147,7 @@
|
||||
clickable={false}
|
||||
style="warning"
|
||||
kind="soft"
|
||||
help="Uncommitted changes"
|
||||
tooltip="Uncommitted changes"
|
||||
>
|
||||
{uncommittedChanges}
|
||||
{uncommittedChanges === 1 ? 'change' : 'changes'}
|
||||
@ -198,7 +198,7 @@
|
||||
clickable={false}
|
||||
icon="locked-small"
|
||||
style="warning"
|
||||
help="Applying this branch will add merge conflict markers that you will have to resolve"
|
||||
tooltip="Applying this branch will add merge conflict markers that you will have to resolve"
|
||||
>
|
||||
Conflict
|
||||
</Button>
|
||||
@ -214,7 +214,7 @@
|
||||
<Button
|
||||
style="pop"
|
||||
kind="soft"
|
||||
help="New changes will land here"
|
||||
tooltip="New changes will land here"
|
||||
icon="target"
|
||||
clickable={false}
|
||||
>
|
||||
@ -224,7 +224,7 @@
|
||||
<Button
|
||||
style="ghost"
|
||||
outline
|
||||
help="When selected, new changes will land here"
|
||||
tooltip="When selected, new changes land here"
|
||||
icon="target"
|
||||
onclick={async () => {
|
||||
isTargetBranchAnimated = true;
|
||||
@ -242,7 +242,7 @@
|
||||
<PullRequestButton
|
||||
click={async ({ draft }) => await createPr({ draft })}
|
||||
disabled={branch.commits.length === 0 || !$gitHost}
|
||||
help={!$gitHost ? 'You can enable git host integration in the settings' : ''}
|
||||
tooltip={!$gitHost ? 'You can enable git host integration in the settings' : ''}
|
||||
loading={isLoading}
|
||||
/>
|
||||
{/if}
|
||||
|
@ -14,6 +14,7 @@
|
||||
import { VirtualBranch } from '$lib/vbranches/types';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
|
||||
interface Props {
|
||||
contextMenuEl?: ContextMenu;
|
||||
@ -138,13 +139,9 @@
|
||||
|
||||
<ContextMenuSection>
|
||||
<ContextMenuItem label="Allow rebasing" on:click={toggleAllowRebasing}>
|
||||
<Toggle
|
||||
small
|
||||
slot="control"
|
||||
bind:checked={allowRebasing}
|
||||
on:click={toggleAllowRebasing}
|
||||
help="Allows changing commits after push (force push needed)"
|
||||
/>
|
||||
<Tooltip slot="control" text={'Allows changing commits after push\n(force push needed)'}>
|
||||
<Toggle small bind:checked={allowRebasing} on:click={toggleAllowRebasing} />
|
||||
</Tooltip>
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSection>
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
import type { Branch } from '$lib/vbranches/types';
|
||||
import { goto } from '$app/navigation';
|
||||
@ -40,13 +40,18 @@
|
||||
<BranchLabel disabled name={branch.name} />
|
||||
<div class="header__remote-branch">
|
||||
{#if remoteBranch}
|
||||
<div
|
||||
class="status-tag text-11 text-semibold remote"
|
||||
use:tooltip={'At least some of your changes have been pushed'}
|
||||
>
|
||||
<Icon name="remote-branch-small" />
|
||||
{localBranch ? 'local and remote' : 'remote'}
|
||||
</div>
|
||||
<Tooltip text="At least some of your changes have been pushed'">
|
||||
<Button
|
||||
size="tag"
|
||||
icon="remote-branch-small"
|
||||
style="neutral"
|
||||
kind="solid"
|
||||
clickable={false}
|
||||
>
|
||||
{localBranch ? 'local and remote' : 'remote'}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{#if gitHostBranch}
|
||||
<Button
|
||||
size="tag"
|
||||
@ -93,7 +98,7 @@
|
||||
<Button
|
||||
style="ghost"
|
||||
outline
|
||||
help="Restores these changes into your working directory"
|
||||
tooltip="Restores these changes into your working directory"
|
||||
icon="plus-small"
|
||||
loading={isApplying}
|
||||
disabled={$mode?.type !== 'OpenWorkspace'}
|
||||
@ -120,7 +125,7 @@
|
||||
<Button
|
||||
style="ghost"
|
||||
outline
|
||||
help="Deletes the local branch. If this branch is also present on a remote, it will not be deleted there."
|
||||
tooltip="Deletes the local branch. If this branch is also present on a remote, it will not be deleted there."
|
||||
icon="bin-small"
|
||||
loading={isDeleting}
|
||||
disabled={!localBranch}
|
||||
|
@ -26,8 +26,8 @@
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import { getTimeAgo } from '@gitbutler/ui/utils/timeAgo';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import { type Snippet } from 'svelte';
|
||||
|
||||
export let branch: VirtualBranch | undefined = undefined;
|
||||
@ -299,24 +299,25 @@
|
||||
|
||||
<div class="text-11 commit__subtitle">
|
||||
{#if commit.isSigned}
|
||||
<div class="commit__signed" use:tooltip={{ text: 'Signed', delay: 500 }}>
|
||||
<Icon name="success-outline-small" />
|
||||
</div>
|
||||
<Tooltip text="Signed">
|
||||
<div class="commit__signed">
|
||||
<Icon name="success-outline-small" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<span class="commit__subtitle-divider">•</span>
|
||||
{/if}
|
||||
|
||||
{#if conflicted}
|
||||
<div
|
||||
class="commit__conflicted"
|
||||
use:tooltip={{
|
||||
text: 'Conflicted commits must be resolved before they can be ammended or squashed.\n\nPlease resolve conflicts using the "Resolve conflicts" button'
|
||||
}}
|
||||
<Tooltip
|
||||
text={"Conflicted commits must be resolved before they can be ammended or squashed.\nPlease resolve conflicts using the 'Resolve conflicts' button"}
|
||||
>
|
||||
<Icon name="warning-small" />
|
||||
<div class="commit__conflicted">
|
||||
<Icon name="warning-small" />
|
||||
|
||||
Conflicted
|
||||
</div>
|
||||
Conflicted
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<span class="commit__subtitle-divider">•</span>
|
||||
{/if}
|
||||
@ -593,6 +594,7 @@
|
||||
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
transition: color var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
color: var(--clr-text-1);
|
||||
|
@ -3,11 +3,11 @@
|
||||
import { persistedCommitMessage, projectRunCommitHooks } from '$lib/config/config';
|
||||
import { getContext, getContextStore } from '$lib/utils/context';
|
||||
import { intersectionObserver } from '$lib/utils/intersectionObserver';
|
||||
import { slideFade } from '$lib/utils/svelteTransitions';
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
import { Ownership } from '$lib/vbranches/ownership';
|
||||
import { VirtualBranch } from '$lib/vbranches/types';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import { slideFade } from '@gitbutler/ui/utils/transitions';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let projectId: string;
|
||||
|
@ -228,7 +228,7 @@
|
||||
wide
|
||||
loading={isPushingCommits}
|
||||
disabled={localCommitsConflicted}
|
||||
help={localCommitsConflicted
|
||||
tooltip={localCommitsConflicted
|
||||
? 'In order to push, please resolve any conflicted commits.'
|
||||
: undefined}
|
||||
onclick={async () => {
|
||||
|
@ -21,7 +21,7 @@
|
||||
import { VirtualBranch, LocalFile } from '$lib/vbranches/types';
|
||||
import Checkbox from '@gitbutler/ui/Checkbox.svelte';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
|
||||
@ -179,57 +179,52 @@
|
||||
{/if}
|
||||
|
||||
{#if title.length > 50}
|
||||
<div
|
||||
transition:fly={{ y: 2, duration: 150 }}
|
||||
class="commit-box__textarea-tooltip"
|
||||
use:tooltip={{
|
||||
text: '50 characters or less is best. Extra info can be added in the description.',
|
||||
delay: 200
|
||||
}}
|
||||
>
|
||||
<Icon name="idea" />
|
||||
</div>
|
||||
<Tooltip text={'50 characters or less is best.\nUse description for more details'}>
|
||||
<div transition:fly={{ y: 2, duration: 150 }} class="commit-box__textarea-tooltip">
|
||||
<Icon name="idea" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="commit-box__texarea-actions"
|
||||
class:commit-box-actions_expanded={isExpanded}
|
||||
use:tooltip={!aiConfigurationValid
|
||||
? 'You must be logged in or have provided your own API key to use this feature'
|
||||
<Tooltip
|
||||
text={!aiConfigurationValid
|
||||
? 'You must be logged in or have provided your own API key'
|
||||
: !$aiGenEnabled
|
||||
? 'You must have summary generation enabled to use this feature'
|
||||
: ''}
|
||||
? 'You must have summary generation enabled'
|
||||
: undefined}
|
||||
>
|
||||
<DropDownButton
|
||||
style="ghost"
|
||||
outline
|
||||
icon="ai-small"
|
||||
disabled={!($aiGenEnabled && aiConfigurationValid)}
|
||||
loading={aiLoading}
|
||||
menuPosition="top"
|
||||
onclick={async () => await generateCommitMessage($branch.files)}
|
||||
>
|
||||
Generate message
|
||||
<div class="commit-box__texarea-actions" class:commit-box-actions_expanded={isExpanded}>
|
||||
<DropDownButton
|
||||
style="ghost"
|
||||
outline
|
||||
icon="ai-small"
|
||||
disabled={!($aiGenEnabled && aiConfigurationValid)}
|
||||
loading={aiLoading}
|
||||
menuPosition="top"
|
||||
onclick={async () => await generateCommitMessage($branch.files)}
|
||||
>
|
||||
Generate message
|
||||
|
||||
{#snippet contextMenuSlot()}
|
||||
<ContextMenuSection>
|
||||
<ContextMenuItem
|
||||
label="Extra concise"
|
||||
on:click={() => ($commitGenerationExtraConcise = !$commitGenerationExtraConcise)}
|
||||
>
|
||||
<Checkbox small slot="control" bind:checked={$commitGenerationExtraConcise} />
|
||||
</ContextMenuItem>
|
||||
{#snippet contextMenuSlot()}
|
||||
<ContextMenuSection>
|
||||
<ContextMenuItem
|
||||
label="Extra concise"
|
||||
on:click={() => ($commitGenerationExtraConcise = !$commitGenerationExtraConcise)}
|
||||
>
|
||||
<Checkbox small slot="control" bind:checked={$commitGenerationExtraConcise} />
|
||||
</ContextMenuItem>
|
||||
|
||||
<ContextMenuItem
|
||||
label="Use emojis 😎"
|
||||
on:click={() => ($commitGenerationUseEmojis = !$commitGenerationUseEmojis)}
|
||||
>
|
||||
<Checkbox small slot="control" bind:checked={$commitGenerationUseEmojis} />
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSection>
|
||||
{/snippet}
|
||||
</DropDownButton>
|
||||
</div>
|
||||
<ContextMenuItem
|
||||
label="Use emojis 😎"
|
||||
on:click={() => ($commitGenerationUseEmojis = !$commitGenerationUseEmojis)}
|
||||
>
|
||||
<Checkbox small slot="control" bind:checked={$commitGenerationUseEmojis} />
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSection>
|
||||
{/snippet}
|
||||
</DropDownButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Checkbox from '@gitbutler/ui/Checkbox.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type { BaseBranch } from '$lib/baseBranch/baseBranch';
|
||||
|
||||
export let base: BaseBranch;
|
||||
@ -49,7 +49,7 @@
|
||||
<Button
|
||||
style="pop"
|
||||
kind="solid"
|
||||
help={`Merges the commits from ${base.branchName} into the base of all applied virtual branches`}
|
||||
tooltip={`Merges the commits from ${base.branchName} into the base of all applied virtual branches`}
|
||||
disabled={$mode?.type !== 'OpenWorkspace'}
|
||||
onclick={() => {
|
||||
if ($mergeUpstreamWarningDismissed) {
|
||||
@ -77,12 +77,9 @@
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<h1
|
||||
class="text-13 info-text text-bold"
|
||||
use:tooltip={'This is the current base for your virtual branches.'}
|
||||
>
|
||||
Local
|
||||
</h1>
|
||||
<Tooltip text="Current base for virtual branches.">
|
||||
<h1 class="text-13 info-text text-bold">Local</h1>
|
||||
</Tooltip>
|
||||
{#each base.recentCommits as commit, index}
|
||||
<CommitCard
|
||||
{commit}
|
||||
|
@ -23,7 +23,7 @@
|
||||
icon="plus-small"
|
||||
size="tag"
|
||||
width={26}
|
||||
help="Insert empty commit"
|
||||
tooltip="Insert empty commit"
|
||||
helpShowDelay={500}
|
||||
onclick={() => dispatch('click')}
|
||||
/>
|
||||
|
@ -138,7 +138,7 @@
|
||||
<Button
|
||||
style="pop"
|
||||
kind="solid"
|
||||
help="Does not create a commit. Can be toggled."
|
||||
tooltip="Does not create a commit. Can be toggled."
|
||||
onclick={async () => createRemoteModal?.show()}>Apply from fork</Button
|
||||
>
|
||||
</div>
|
||||
|
@ -24,7 +24,7 @@
|
||||
style="ghost"
|
||||
outline
|
||||
icon="update-small"
|
||||
help="Last fetch from upstream"
|
||||
tooltip="Last fetch from upstream"
|
||||
{loading}
|
||||
onmousedown={async (e) => {
|
||||
e.preventDefault();
|
||||
|
@ -13,7 +13,7 @@
|
||||
size="tag"
|
||||
style="error"
|
||||
kind="solid"
|
||||
help="Merge upstream commits into common base"
|
||||
tooltip="Merge upstream into common base"
|
||||
onclick={async () => {
|
||||
loading = true;
|
||||
try {
|
||||
|
@ -35,7 +35,7 @@
|
||||
clickable={false}
|
||||
icon="locked-small"
|
||||
style="warning"
|
||||
help="File changes cannot be moved because part of this file was already committed into this branch"
|
||||
tooltip="File changes cannot be moved because part of this file was already committed into this branch"
|
||||
>Locked</Button
|
||||
>
|
||||
{/if}
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { getLocalCommits, getLocalAndRemoteCommits } from '$lib/vbranches/contexts';
|
||||
import { getLockText } from '$lib/vbranches/tooltip';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type { HunkSection, ContentSection } from '$lib/utils/fileSections';
|
||||
|
||||
interface Props {
|
||||
@ -76,14 +76,9 @@
|
||||
</div>
|
||||
|
||||
{#if section.hunk.lockedTo && section.hunk.lockedTo.length > 0 && commits}
|
||||
<div
|
||||
use:tooltip={{
|
||||
text: getLockText(section.hunk.lockedTo, commits),
|
||||
delay: 500
|
||||
}}
|
||||
>
|
||||
<Tooltip text={getLockText(section.hunk.lockedTo, commits)}>
|
||||
<Icon name="locked-small" color="warning" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{#if section.hunk.poisoned}
|
||||
Can not manage this hunk because it depends on changes from multiple branches
|
||||
|
@ -172,7 +172,7 @@
|
||||
size="tag"
|
||||
style="ghost"
|
||||
outline
|
||||
help="Restores GitButler and your files to the state before this operation. Revert actions can also be undone."
|
||||
tooltip="Restores GitButler and your files to the state before this operation. Revert actions can also be undone."
|
||||
onclick={() => {
|
||||
dispatch('restoreClick');
|
||||
}}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import { type Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
@ -21,16 +21,17 @@
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
use:tooltip={isNavCollapsed ? tooltipLabel : ''}
|
||||
{onmousedown}
|
||||
class="domain-button text-14 text-semibold"
|
||||
class:selected={isSelected}
|
||||
class:align-center={alignItems === 'center'}
|
||||
class:align-top={alignItems === 'top'}
|
||||
>
|
||||
{@render children()}
|
||||
</button>
|
||||
<Tooltip text={isNavCollapsed ? tooltipLabel : ''} align="start">
|
||||
<button
|
||||
{onmousedown}
|
||||
class="domain-button text-14 text-semibold"
|
||||
class:selected={isSelected}
|
||||
class:align-center={alignItems === 'center'}
|
||||
class:align-top={alignItems === 'top'}
|
||||
>
|
||||
{@render children()}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<style lang="postcss">
|
||||
.domain-button {
|
||||
|
@ -19,7 +19,9 @@
|
||||
icon="mail"
|
||||
style="ghost"
|
||||
size="cta"
|
||||
help="Share feedback"
|
||||
tooltip="Share feedback"
|
||||
tooltipAlign="start"
|
||||
tooltipPosition={isNavCollapsed ? 'bottom' : 'top'}
|
||||
onclick={() => events.emit('openSendIssueModal')}
|
||||
wide={isNavCollapsed}
|
||||
/>
|
||||
@ -27,7 +29,9 @@
|
||||
icon="settings"
|
||||
style="ghost"
|
||||
size="cta"
|
||||
help="Project settings"
|
||||
tooltip="Project settings"
|
||||
tooltipAlign={isNavCollapsed ? 'start' : 'center'}
|
||||
tooltipPosition={isNavCollapsed ? 'bottom' : 'top'}
|
||||
onclick={async () => await goto(`/${projectId}/settings`)}
|
||||
wide={isNavCollapsed}
|
||||
disabled={$mode?.type !== 'OpenWorkspace'}
|
||||
@ -36,7 +40,9 @@
|
||||
icon="timeline"
|
||||
style="ghost"
|
||||
size="cta"
|
||||
help="Project history"
|
||||
tooltip="Project history"
|
||||
tooltipAlign={isNavCollapsed ? 'start' : 'center'}
|
||||
tooltipPosition={isNavCollapsed ? 'bottom' : 'top'}
|
||||
onclick={() => events.emit('openHistory')}
|
||||
wide={isNavCollapsed}
|
||||
/>
|
||||
|
@ -4,7 +4,7 @@
|
||||
import ProjectAvatar from '$lib/navigation/ProjectAvatar.svelte';
|
||||
import { getContext } from '$lib/utils/context';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
|
||||
export let isNavCollapsed: boolean;
|
||||
|
||||
@ -15,23 +15,24 @@
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<button
|
||||
bind:this={buttonTrigger}
|
||||
class="text-input button"
|
||||
use:tooltip={isNavCollapsed ? project?.title : ''}
|
||||
on:mousedown={(e) => {
|
||||
e.preventDefault();
|
||||
popup.toggle();
|
||||
}}
|
||||
>
|
||||
<ProjectAvatar name={project?.title} />
|
||||
{#if !isNavCollapsed}
|
||||
<span class="button__label text-14 text-bold">{project?.title}</span>
|
||||
<div class="button__icon">
|
||||
<Icon name="select-chevron" />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
<Tooltip text={isNavCollapsed ? project?.title : ''} align="start">
|
||||
<button
|
||||
bind:this={buttonTrigger}
|
||||
class="text-input button"
|
||||
on:mousedown={(e) => {
|
||||
e.preventDefault();
|
||||
popup.toggle();
|
||||
}}
|
||||
>
|
||||
<ProjectAvatar name={project?.title} />
|
||||
{#if !isNavCollapsed}
|
||||
<span class="button__label text-14 text-bold">{project?.title}</span>
|
||||
<div class="button__icon">
|
||||
<Icon name="select-chevron" />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<ProjectsPopup bind:this={popup} target={buttonTrigger} {isNavCollapsed} />
|
||||
</div>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
import { getContext } from '$lib/utils/context';
|
||||
import Badge from '@gitbutler/ui/Badge.svelte';
|
||||
import Icon from '@gitbutler/ui/Icon.svelte';
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
@ -38,12 +38,13 @@
|
||||
{#if !isNavCollapsed}
|
||||
<div class="content">
|
||||
<div class="button-head">
|
||||
<span
|
||||
use:tooltip={'The branch that your Workspace virtual branches are based on and will be merged into.'}
|
||||
class="text-14 text-semibold trunk-label">Target</span
|
||||
>
|
||||
<Tooltip text="The branch your Workspace branches are based on and merge into.">
|
||||
<span class="text-14 text-semibold trunk-label">Target</span>
|
||||
</Tooltip>
|
||||
{#if ($base?.behind || 0) > 0}
|
||||
<Badge label={$base?.behind || 0} help="Unmerged upstream commits" />
|
||||
<Tooltip text="Unmerged upstream commits">
|
||||
<Badge label={$base?.behind || 0} />
|
||||
</Tooltip>
|
||||
{/if}
|
||||
<SyncButton />
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
export let loading = false;
|
||||
export let disabled = false;
|
||||
export let wide = false;
|
||||
export let help = '';
|
||||
export let tooltip = '';
|
||||
|
||||
function persistedAction(projectId: string): Persisted<MergeMethod> {
|
||||
const key = 'projectMergeMethod';
|
||||
@ -35,7 +35,7 @@
|
||||
{loading}
|
||||
bind:this={dropDown}
|
||||
{wide}
|
||||
{help}
|
||||
{tooltip}
|
||||
{disabled}
|
||||
onclick={() => {
|
||||
dispatch('click', { method: $action });
|
||||
|
@ -20,10 +20,10 @@
|
||||
type Props = {
|
||||
loading: boolean;
|
||||
disabled: boolean;
|
||||
help: string;
|
||||
tooltip: string;
|
||||
click: (opts: { draft: boolean }) => void;
|
||||
};
|
||||
const { loading, disabled, help, click }: Props = $props();
|
||||
const { loading, disabled, tooltip, click }: Props = $props();
|
||||
|
||||
const preferredAction = persisted<Action>(Action.Create, 'projectDefaultPrAction');
|
||||
let dropDown: DropDownButton;
|
||||
@ -38,7 +38,7 @@
|
||||
<DropDownButton
|
||||
style="ghost"
|
||||
outline
|
||||
{help}
|
||||
{tooltip}
|
||||
{disabled}
|
||||
{loading}
|
||||
bind:this={dropDown}
|
||||
|
@ -169,7 +169,7 @@
|
||||
style="ghost"
|
||||
outline
|
||||
loading={$mrLoading || $checksLoading}
|
||||
help={$timeAgo ? 'Updated ' + $timeAgo : ''}
|
||||
tooltip={$timeAgo ? 'Updated ' + $timeAgo : ''}
|
||||
onclick={async () => {
|
||||
$checksMonitor?.update();
|
||||
$prMonitor?.refresh();
|
||||
@ -231,7 +231,7 @@
|
||||
!$pr.mergeable ||
|
||||
['dirty', 'unknown', 'blocked', 'behind'].includes($pr.mergeableState)}
|
||||
loading={isMerging}
|
||||
help="Merge pull request and refresh"
|
||||
tooltip="Merge pull request and refresh"
|
||||
on:click={async (e) => {
|
||||
if (!$pr) return;
|
||||
isMerging = true;
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ContextMenu from '$lib/components/contextmenu/ContextMenu.svelte';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type iconsJson from '@gitbutler/ui/data/icons.json';
|
||||
import type { ComponentColor, ComponentStyleKind } from '@gitbutler/ui/utils/colorTypes';
|
||||
import type { Snippet } from 'svelte';
|
||||
@ -13,7 +14,7 @@
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
wide?: boolean;
|
||||
help?: string;
|
||||
tooltip?: string;
|
||||
menuPosition?: 'top' | 'bottom';
|
||||
children: Snippet;
|
||||
contextMenuSlot: Snippet;
|
||||
@ -28,7 +29,7 @@
|
||||
disabled = false,
|
||||
loading = false,
|
||||
wide = false,
|
||||
help = '',
|
||||
tooltip,
|
||||
menuPosition = 'bottom',
|
||||
children,
|
||||
contextMenuSlot,
|
||||
@ -50,48 +51,48 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="dropdown-wrapper" class:wide>
|
||||
<div class="dropdown">
|
||||
<Button
|
||||
{style}
|
||||
{icon}
|
||||
{kind}
|
||||
{help}
|
||||
{outline}
|
||||
reversedDirection
|
||||
disabled={disabled || loading}
|
||||
isDropdownChild
|
||||
{onclick}
|
||||
>
|
||||
{@render children()}
|
||||
</Button>
|
||||
<Button
|
||||
bind:el={iconEl}
|
||||
{style}
|
||||
{kind}
|
||||
{help}
|
||||
{outline}
|
||||
icon={visible ? 'chevron-up' : 'chevron-down'}
|
||||
{loading}
|
||||
disabled={disabled || loading}
|
||||
isDropdownChild
|
||||
onclick={() => {
|
||||
visible = !visible;
|
||||
contextMenu?.toggle();
|
||||
<Tooltip text={tooltip}>
|
||||
<div class="dropdown-wrapper" class:wide>
|
||||
<div class="dropdown">
|
||||
<Button
|
||||
{style}
|
||||
{icon}
|
||||
{kind}
|
||||
{outline}
|
||||
reversedDirection
|
||||
disabled={disabled || loading}
|
||||
dropdownChild
|
||||
{onclick}
|
||||
>
|
||||
{@render children()}
|
||||
</Button>
|
||||
<Button
|
||||
bind:el={iconEl}
|
||||
{style}
|
||||
{kind}
|
||||
{outline}
|
||||
icon={visible ? 'chevron-up' : 'chevron-down'}
|
||||
{loading}
|
||||
disabled={disabled || loading}
|
||||
dropdownChild
|
||||
onclick={() => {
|
||||
visible = !visible;
|
||||
contextMenu?.toggle();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ContextMenu
|
||||
bind:this={contextMenu}
|
||||
target={iconEl}
|
||||
verticalAlign={menuPosition}
|
||||
onclose={() => {
|
||||
visible = false;
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{@render contextMenuSlot()}
|
||||
</ContextMenu>
|
||||
</div>
|
||||
<ContextMenu
|
||||
bind:this={contextMenu}
|
||||
target={iconEl}
|
||||
verticalAlign={menuPosition}
|
||||
onclose={() => {
|
||||
visible = false;
|
||||
}}
|
||||
>
|
||||
{@render contextMenuSlot()}
|
||||
</ContextMenu>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<style lang="postcss">
|
||||
.dropdown-wrapper {
|
||||
|
@ -1,11 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
|
||||
export let small = false;
|
||||
export let disabled = false;
|
||||
export let checked = false;
|
||||
export let value = '';
|
||||
export let help = '';
|
||||
export let id = '';
|
||||
</script>
|
||||
|
||||
@ -18,7 +15,6 @@
|
||||
{value}
|
||||
{id}
|
||||
{disabled}
|
||||
use:tooltip={help}
|
||||
/>
|
||||
|
||||
<style lang="postcss">
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { pxToRem } from '@gitbutler/ui/utils/pxToRem';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import { slide, type SlideParams, type TransitionConfig } from 'svelte/transition';
|
||||
|
||||
export function slideFade(node: Element, options: SlideParams): TransitionConfig {
|
||||
const slideTrans: TransitionConfig = slide(node, options);
|
||||
|
||||
return {
|
||||
...slideTrans,
|
||||
css: (t, u) =>
|
||||
`${slideTrans.css ? slideTrans.css(t, u) : ''}
|
||||
opacity:${t};`
|
||||
};
|
||||
}
|
||||
|
||||
// extend SlideParams with opacity
|
||||
type SlideFadeParams = SlideParams & {
|
||||
opacity?: number;
|
||||
minHeight?: number;
|
||||
animateTopPadding?: boolean;
|
||||
animateBottomPadding?: boolean;
|
||||
animateLeftPadding?: boolean;
|
||||
animateRightPadding?: boolean;
|
||||
};
|
||||
|
||||
export function slideFadeExt(node: Element, options: SlideFadeParams): TransitionConfig {
|
||||
const slideTrans: TransitionConfig = slide(node, options);
|
||||
|
||||
const currentHeight = node.clientHeight;
|
||||
const minHeight = options.minHeight || 0;
|
||||
|
||||
const currentPadding = {
|
||||
top: pxToRem(+getComputedStyle(node).paddingTop),
|
||||
bottom: pxToRem(+getComputedStyle(node).paddingBottom),
|
||||
left: pxToRem(+getComputedStyle(node).paddingLeft),
|
||||
right: pxToRem(+getComputedStyle(node).paddingRight)
|
||||
};
|
||||
|
||||
return {
|
||||
...slideTrans,
|
||||
easing: sineInOut,
|
||||
css: (t) =>
|
||||
`height: ${minHeight + (currentHeight - minHeight) * Math.min(Math.max(t, 0), 1)}px;
|
||||
opacity:${t};
|
||||
padding-top: ${options.animateTopPadding ? 0 : currentPadding.top};
|
||||
padding-bottom: ${options.animateBottomPadding ? 0 : currentPadding.bottom};
|
||||
padding-left: ${options.animateLeftPadding ? 0 : currentPadding.left};
|
||||
padding-right: ${options.animateRightPadding ? 0 : currentPadding.right};
|
||||
`
|
||||
};
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
"package:svelte": "svelte-kit sync && svelte-package",
|
||||
"package:styles": "postcss ./src/styles/main.css -o ./dist/styles/main.css && pnpm run copy-fonts",
|
||||
"copy-fonts": "postcss ./src/styles/fonts.css -o ./dist/styles/fonts.css && cpy './src/styles/fonts/**/*.woff2' './dist/styles/fonts' --parents",
|
||||
"design-tokens:build": "npx tz build && prettier --write ./src/lib/design-tokens.json ./src/styles/core/design-tokens.css",
|
||||
"design-tokens:build": "npx tz build && prettier --write ./src/lib/data/design-tokens.json ./src/styles/core/design-tokens.css",
|
||||
"prepublishOnly": "pnpm run package",
|
||||
"prepare": "svelte-kit sync",
|
||||
"storybook": "storybook dev --no-open -p 6006",
|
||||
|
@ -3,18 +3,16 @@
|
||||
|
||||
export interface BadgeProps {
|
||||
label: string | number;
|
||||
help?: string;
|
||||
style?: ComponentColor;
|
||||
kind?: ComponentStyleKind;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
let { label, help, style = 'neutral', kind = 'solid' }: BadgeProps = $props();
|
||||
const { label, style = 'neutral', kind = 'solid' }: BadgeProps = $props();
|
||||
</script>
|
||||
|
||||
<div class="badge {style} {kind} text-10 text-semibold" use:tooltip={help}>
|
||||
<div class="badge {style} {kind} text-10 text-semibold">
|
||||
{label}
|
||||
</div>
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
import type { ComponentColor, ComponentStyleKind } from '$lib/utils/colorTypes';
|
||||
|
||||
export interface ButtonProps {
|
||||
el?: HTMLElement;
|
||||
// Interaction props
|
||||
@ -18,7 +16,7 @@
|
||||
wide?: boolean;
|
||||
grow?: boolean;
|
||||
align?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline' | 'auto';
|
||||
isDropdownChild?: boolean;
|
||||
dropdownChild?: boolean;
|
||||
// Style props
|
||||
style?: ComponentColor;
|
||||
kind?: ComponentStyleKind;
|
||||
@ -27,7 +25,9 @@
|
||||
solidBackground?: boolean;
|
||||
// Additional elements
|
||||
icon?: keyof typeof iconsJson | undefined;
|
||||
help?: string;
|
||||
tooltip?: string;
|
||||
tooltipPosition?: TooltipPosition;
|
||||
tooltipAlign?: TooltipAlign;
|
||||
helpShowDelay?: number;
|
||||
testId?: string;
|
||||
// Events
|
||||
@ -41,10 +41,11 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import Tooltip, { type TooltipAlign, type TooltipPosition } from './Tooltip.svelte';
|
||||
import Icon from '$lib/Icon.svelte';
|
||||
import { pxToRem } from '$lib/utils/pxToRem';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import type iconsJson from '$lib/data/icons.json';
|
||||
import type { ComponentColor, ComponentStyleKind } from '$lib/utils/colorTypes';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
@ -62,7 +63,7 @@
|
||||
wide = false,
|
||||
grow = false,
|
||||
align = 'auto',
|
||||
isDropdownChild = false,
|
||||
dropdownChild = false,
|
||||
style = 'neutral',
|
||||
kind = 'soft',
|
||||
outline = false,
|
||||
@ -70,8 +71,9 @@
|
||||
solidBackground = false,
|
||||
testId,
|
||||
icon,
|
||||
help = '',
|
||||
helpShowDelay = 1200,
|
||||
tooltip,
|
||||
tooltipPosition,
|
||||
tooltipAlign,
|
||||
onclick,
|
||||
onmousedown,
|
||||
oncontextmenu,
|
||||
@ -89,55 +91,53 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
bind:this={el}
|
||||
class="btn focus-state {style} {kind} {size}-size"
|
||||
class:outline
|
||||
class:dashed
|
||||
class:solidBackground
|
||||
class:reversed-direction={reversedDirection}
|
||||
class:shrinkable
|
||||
class:wide
|
||||
class:grow
|
||||
class:not-clickable={!clickable}
|
||||
class:fixed-width={!children && !wide}
|
||||
class:is-dropdown={isDropdownChild}
|
||||
style:align-self={align}
|
||||
style:width={width ? pxToRem(width) : undefined}
|
||||
use:tooltip={{
|
||||
text: help,
|
||||
delay: helpShowDelay
|
||||
}}
|
||||
disabled={disabled || loading}
|
||||
onclick={handleAction}
|
||||
{onmousedown}
|
||||
{oncontextmenu}
|
||||
{onkeydown}
|
||||
{type}
|
||||
{id}
|
||||
{...testId ? { 'data-testid': testId } : null}
|
||||
tabindex={clickable ? tabindex : -1}
|
||||
>
|
||||
{#if children}
|
||||
<span
|
||||
class="label text-semibold"
|
||||
class:text-12={size === 'button' || size === 'cta'}
|
||||
class:text-11={size === 'tag'}
|
||||
>
|
||||
{@render children()}
|
||||
</span>
|
||||
{/if}
|
||||
<Tooltip text={tooltip} align={tooltipAlign} position={tooltipPosition}>
|
||||
<button
|
||||
bind:this={el}
|
||||
class="btn focus-state {style} {kind} {size}-size"
|
||||
class:outline
|
||||
class:dashed
|
||||
class:solidBackground
|
||||
class:reversed-direction={reversedDirection}
|
||||
class:shrinkable
|
||||
class:wide
|
||||
class:grow
|
||||
class:is-dropdown={dropdownChild}
|
||||
class:not-clickable={!clickable}
|
||||
class:fixed-width={!children && !wide}
|
||||
style:align-self={align}
|
||||
style:width={width ? pxToRem(width) : undefined}
|
||||
disabled={disabled || loading}
|
||||
onclick={handleAction}
|
||||
{onmousedown}
|
||||
{oncontextmenu}
|
||||
{onkeydown}
|
||||
{type}
|
||||
{id}
|
||||
{...testId ? { 'data-testid': testId } : null}
|
||||
tabindex={clickable ? tabindex : -1}
|
||||
>
|
||||
{#if children}
|
||||
<span
|
||||
class="label text-semibold"
|
||||
class:text-12={size === 'button' || size === 'cta'}
|
||||
class:text-11={size === 'tag'}
|
||||
>
|
||||
{@render children()}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if icon || loading}
|
||||
<div class="btn-icon">
|
||||
{#if loading}
|
||||
<Icon name="spinner" spinnerRadius={4.5} />
|
||||
{:else if icon}
|
||||
<Icon name={icon} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
{#if icon || loading}
|
||||
<div class="btn-icon">
|
||||
{#if loading}
|
||||
<Icon name="spinner" spinnerRadius={4.5} />
|
||||
{:else if icon}
|
||||
<Icon name={icon} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<style lang="postcss">
|
||||
.btn {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/Icon.svelte';
|
||||
import TimeAgo from '$lib/TimeAgo.svelte';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
@ -55,8 +55,6 @@
|
||||
observer.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
const tooltipDelay = 500;
|
||||
</script>
|
||||
|
||||
<button class="branch" class:selected onmousedown={onMouseDown} bind:this={intersectionTarget}>
|
||||
@ -84,17 +82,18 @@
|
||||
|
||||
<div class="row-group">
|
||||
{#if pullRequestDetails}
|
||||
<div
|
||||
use:tooltip={{ text: pullRequestDetails.title, delay: tooltipDelay }}
|
||||
class="branch-tag tag-pr"
|
||||
class:tag-pr={!pullRequestDetails.draft}
|
||||
class:tag-draft-pr={pullRequestDetails.draft}
|
||||
>
|
||||
<span class="text-10 text-semibold">
|
||||
{#if !pullRequestDetails.draft}PR{:else}Draft{/if}
|
||||
</span>
|
||||
<Icon name="pr-small" />
|
||||
</div>
|
||||
<Tooltip text={pullRequestDetails.title}>
|
||||
<div
|
||||
class="branch-tag tag-pr"
|
||||
class:tag-pr={!pullRequestDetails.draft}
|
||||
class:tag-draft-pr={pullRequestDetails.draft}
|
||||
>
|
||||
<span class="text-10 text-semibold">
|
||||
{#if !pullRequestDetails.draft}PR{:else}Draft{/if}
|
||||
</span>
|
||||
<Icon name="pr-small" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{#if applied}
|
||||
<div class="branch-tag tag-applied">
|
||||
@ -106,15 +105,14 @@
|
||||
|
||||
<div class="row">
|
||||
{#if lastCommitDetails?.lastCommitAt}
|
||||
<span
|
||||
class="branch-time text-11 details truncate"
|
||||
use:tooltip={lastCommitDetails.lastCommitAt.toLocaleString('en-GB')}
|
||||
>
|
||||
{#if lastCommitDetails}
|
||||
<TimeAgo date={lastCommitDetails.lastCommitAt} addSuffix />
|
||||
by {lastCommitDetails.authorName}
|
||||
{/if}
|
||||
</span>
|
||||
<Tooltip text={lastCommitDetails.lastCommitAt.toLocaleString('en-GB')}>
|
||||
<span class="branch-time text-11 details truncate">
|
||||
{#if lastCommitDetails}
|
||||
<TimeAgo date={lastCommitDetails.lastCommitAt} addSuffix />
|
||||
by {lastCommitDetails.authorName}
|
||||
{/if}
|
||||
</span>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<span class="branch-time text-11 details truncate">
|
||||
{#if lastCommitDetails}
|
||||
@ -125,38 +123,30 @@
|
||||
|
||||
<div class="stats">
|
||||
{#if branchDetails}
|
||||
<div
|
||||
use:tooltip={{
|
||||
text: 'Code changes',
|
||||
delay: tooltipDelay
|
||||
}}
|
||||
class="code-changes"
|
||||
>
|
||||
<span class="text-10 text-semibold">+{branchDetails.linesAdded}</span>
|
||||
<span class="text-10 text-semibold">-{branchDetails.linesRemoved}</span>
|
||||
</div>
|
||||
<Tooltip text="Code changes">
|
||||
<div class="code-changes">
|
||||
<span class="text-10 text-semibold">+{branchDetails.linesAdded}</span>
|
||||
<span class="text-10 text-semibold">-{branchDetails.linesRemoved}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<div
|
||||
use:tooltip={{
|
||||
text: 'Number of commits',
|
||||
delay: tooltipDelay
|
||||
}}
|
||||
class="branch-tag tag-commits"
|
||||
>
|
||||
<svg
|
||||
width="12"
|
||||
height="8"
|
||||
viewBox="0 0 12 8"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="6.16675" cy="4" r="2.5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M8.66675 4H12.0001" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M0.333374 4H3.66671" stroke="currentColor" stroke-width="1.5" />
|
||||
</svg>
|
||||
<Tooltip text="Number of commits">
|
||||
<div class="branch-tag tag-commits">
|
||||
<svg
|
||||
width="12"
|
||||
height="8"
|
||||
viewBox="0 0 12 8"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="6.16675" cy="4" r="2.5" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M8.66675 4H12.0001" stroke="currentColor" stroke-width="1.5" />
|
||||
<path d="M0.333374 4H3.66671" stroke="currentColor" stroke-width="1.5" />
|
||||
</svg>
|
||||
|
||||
<span class="text-10 text-semibold">{branchDetails.commitCount}</span>
|
||||
</div>
|
||||
<span class="text-10 text-semibold">{branchDetails.commitCount}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
142
packages/ui/src/lib/Tooltip.svelte
Normal file
142
packages/ui/src/lib/Tooltip.svelte
Normal file
@ -0,0 +1,142 @@
|
||||
<script lang="ts" context="module">
|
||||
export type TooltipPosition = 'top' | 'bottom';
|
||||
export type TooltipAlign = 'start' | 'center' | 'end';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { portal } from '$lib/utils/portal';
|
||||
import { flyScale } from '$lib/utils/transitions';
|
||||
import { type Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
text?: string;
|
||||
delay?: number;
|
||||
align?: TooltipAlign;
|
||||
position?: TooltipPosition;
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
const { text, delay = 700, align = 'center', position = 'bottom', children }: Props = $props();
|
||||
|
||||
let targetEl: HTMLElement | undefined = $state();
|
||||
let tooltipEl: HTMLElement | undefined = $state();
|
||||
|
||||
let show = $state(false);
|
||||
let timeoutId: undefined | ReturnType<typeof setTimeout> = $state();
|
||||
|
||||
const isTextEmpty = $derived(!text || text === '');
|
||||
|
||||
function handleMouseEnter() {
|
||||
timeoutId = setTimeout(() => {
|
||||
show = true;
|
||||
// console.log('showing tooltip');
|
||||
}, delay); // 500ms delay before showing the tooltip
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
clearTimeout(timeoutId);
|
||||
show = false;
|
||||
}
|
||||
|
||||
function adjustPosition() {
|
||||
if (!targetEl || !tooltipEl) return;
|
||||
|
||||
const tooltipRect = tooltipEl.getBoundingClientRect();
|
||||
// get first child of targetEl
|
||||
const targetChild = targetEl.children[0];
|
||||
const targetRect = targetChild.getBoundingClientRect();
|
||||
|
||||
let top = 0;
|
||||
let left = 0;
|
||||
let transformOriginTop = 'center';
|
||||
let transformOriginLeft = 'center';
|
||||
const gap = 4;
|
||||
|
||||
if (position === 'bottom') {
|
||||
top = targetRect.bottom + window.scrollY + gap;
|
||||
|
||||
transformOriginTop = 'top';
|
||||
} else if (position === 'top') {
|
||||
top = targetRect.top - tooltipRect.height + window.scrollY - gap;
|
||||
|
||||
transformOriginTop = 'bottom';
|
||||
}
|
||||
|
||||
if (align === 'start') {
|
||||
left = targetRect.left + window.scrollX;
|
||||
transformOriginLeft = 'left';
|
||||
} else if (align === 'end') {
|
||||
left = targetRect.right - tooltipRect.width + window.scrollX;
|
||||
transformOriginLeft = 'right';
|
||||
} else if (align === 'center') {
|
||||
left = targetRect.left + targetRect.width / 2 - tooltipRect.width / 2 + window.scrollX;
|
||||
transformOriginLeft = 'center';
|
||||
}
|
||||
|
||||
tooltipEl.style.top = `${top}px`;
|
||||
tooltipEl.style.left = `${left}px`;
|
||||
tooltipEl.style.transformOrigin = `${transformOriginTop} ${transformOriginLeft}`;
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (tooltipEl) {
|
||||
adjustPosition();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if isTextEmpty}
|
||||
{@render children()}
|
||||
{:else}
|
||||
<span
|
||||
bind:this={targetEl}
|
||||
class="tooltip-wrap"
|
||||
role="tooltip"
|
||||
onmouseenter={handleMouseEnter}
|
||||
onmouseleave={handleMouseLeave}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
|
||||
{#if show}
|
||||
<div
|
||||
bind:this={tooltipEl}
|
||||
use:portal={'body'}
|
||||
class="tooltip-container text-11 text-body"
|
||||
transition:flyScale={{
|
||||
position: position
|
||||
}}
|
||||
>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.tooltip-wrap {
|
||||
position: relative;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.tooltip-container {
|
||||
white-space: pre-line;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
background-color: var(--clr-tooltip-bg);
|
||||
border: 1px solid var(--clr-tooltip-border);
|
||||
border-radius: var(--radius-m);
|
||||
color: var(--clr-core-ntrl-80);
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
max-width: 240px;
|
||||
padding: 4px 8px;
|
||||
z-index: var(--z-blocker);
|
||||
text-align: left;
|
||||
box-shadow: var(--fx-shadow-s);
|
||||
}
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/Tooltip.svelte';
|
||||
import { stringToColor } from '$lib/utils/stringToColor';
|
||||
import { tooltip as tooltipComponent } from '$lib/utils/tooltip';
|
||||
|
||||
interface Props {
|
||||
srcUrl: string;
|
||||
@ -13,20 +13,18 @@
|
||||
const { srcUrl, tooltip, size = 'small' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="image-wrapper {size}" style:background-color={stringToColor(tooltip)}>
|
||||
<img
|
||||
class="avatar"
|
||||
alt={tooltip}
|
||||
src={srcUrl}
|
||||
loading="lazy"
|
||||
onload={() => (isLoaded = true)}
|
||||
class:show={isLoaded}
|
||||
use:tooltipComponent={{
|
||||
text: tooltip,
|
||||
delay: 500
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Tooltip text={tooltip}>
|
||||
<div class="image-wrapper {size}" style:background-color={stringToColor(tooltip)}>
|
||||
<img
|
||||
class="avatar"
|
||||
alt={tooltip}
|
||||
src={srcUrl}
|
||||
loading="lazy"
|
||||
onload={() => (isLoaded = true)}
|
||||
class:show={isLoaded}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<style lang="postcss">
|
||||
.image-wrapper {
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Avatar from './Avatar.svelte';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import Tooltip from '$lib/Tooltip.svelte';
|
||||
|
||||
const { avatars, maxAvatars = 5 }: Props = $props();
|
||||
|
||||
@ -43,15 +43,11 @@
|
||||
{/if}
|
||||
{/each}
|
||||
{#if avatars.length > maxAvatars}
|
||||
<div
|
||||
class="avatars-counter"
|
||||
use:tooltip={{
|
||||
text: getTooltipText() || 'mr. unknown',
|
||||
delay: 500
|
||||
}}
|
||||
>
|
||||
<span class="text-11 text-semibold">+{avatars.length - maxAvatars}</span>
|
||||
</div>
|
||||
<Tooltip text={getTooltipText() || 'mr. unknown'}>
|
||||
<div class="avatars-counter">
|
||||
<span class="text-11 text-semibold">+{avatars.length - maxAvatars}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/Tooltip.svelte';
|
||||
import Avatar from '$lib/avatar/Avatar.svelte';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import { isDefined } from '$lib/utils/typeguards';
|
||||
import type { CommitNodeData, Color } from '$lib/commitLines/types';
|
||||
|
||||
@ -37,7 +37,9 @@
|
||||
<Avatar srcUrl={commitNode.commit?.author.gravatarUrl ?? ''} tooltip={hoverText} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="small-node" use:tooltip={hoverText}></div>
|
||||
<Tooltip text={hoverText}>
|
||||
<div class="small-node"></div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -795,7 +795,7 @@
|
||||
},
|
||||
"30": {
|
||||
"$type": "color",
|
||||
"$value": "#2a7e57",
|
||||
"$value": "#287b55",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -811,7 +811,7 @@
|
||||
},
|
||||
"40": {
|
||||
"$type": "color",
|
||||
"$value": "#469b73",
|
||||
"$value": "#3c9a6f",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -827,7 +827,7 @@
|
||||
},
|
||||
"50": {
|
||||
"$type": "color",
|
||||
"$value": "#4eb182",
|
||||
"$value": "#4ab582",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -843,7 +843,7 @@
|
||||
},
|
||||
"60": {
|
||||
"$type": "color",
|
||||
"$value": "#a1ceb9",
|
||||
"$value": "#92ddba",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -859,7 +859,7 @@
|
||||
},
|
||||
"70": {
|
||||
"$type": "color",
|
||||
"$value": "#cae8da",
|
||||
"$value": "#bef4da",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -875,7 +875,7 @@
|
||||
},
|
||||
"80": {
|
||||
"$type": "color",
|
||||
"$value": "#d6f0e4",
|
||||
"$value": "#d0f7e5",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -891,7 +891,7 @@
|
||||
},
|
||||
"90": {
|
||||
"$type": "color",
|
||||
"$value": "#e8f7f0",
|
||||
"$value": "#e5faf0",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {},
|
||||
@ -3921,6 +3921,46 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tooltip": {
|
||||
"border": {
|
||||
"$type": "color",
|
||||
"$value": "{clr-core.ntrl.10}",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {
|
||||
"light": "{clr-core.ntrl.10}",
|
||||
"dark": "{clr-core.ntrl.30}"
|
||||
},
|
||||
"figma": {
|
||||
"variableId": "VariableID:4330:3683",
|
||||
"collection": {
|
||||
"id": "VariableCollectionId:8:1868",
|
||||
"name": "clr",
|
||||
"defaultModeId": "8:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bg": {
|
||||
"$type": "color",
|
||||
"$value": "{clr-core.ntrl.10}",
|
||||
"$description": "",
|
||||
"$extensions": {
|
||||
"mode": {
|
||||
"light": "{clr-core.ntrl.10}",
|
||||
"dark": "{clr-core.ntrl.10}"
|
||||
},
|
||||
"figma": {
|
||||
"variableId": "VariableID:4330:3697",
|
||||
"collection": {
|
||||
"id": "VariableCollectionId:8:1868",
|
||||
"name": "clr",
|
||||
"defaultModeId": "8:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
@ -4097,7 +4137,7 @@
|
||||
"useDTCGKeys": true,
|
||||
"colorMode": "hex",
|
||||
"variableCollections": ["clr-core", "clr", "size", "radius"],
|
||||
"createdAt": "2024-08-10T21:58:58.533Z"
|
||||
"createdAt": "2024-08-31T23:16:13.487Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
import Checkbox from '$lib/Checkbox.svelte';
|
||||
import Icon from '$lib/Icon.svelte';
|
||||
import FileIcon from '$lib/file/FileIcon.svelte';
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type { FileStatus } from './types';
|
||||
|
||||
interface Props {
|
||||
@ -95,9 +95,11 @@
|
||||
|
||||
<div class="details">
|
||||
{#if locked}
|
||||
<div class="locked" use:tooltip={{ text: lockText ?? '', delay: 500 }}>
|
||||
<Icon name="locked-small" color="warning" />
|
||||
</div>
|
||||
<Tooltip text={lockText}>
|
||||
<div class="locked">
|
||||
<Icon name="locked-small" color="warning" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if conflicted}
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import type { SegmentContext } from './segmentTypes';
|
||||
import type { Snippet } from 'svelte';
|
||||
@ -8,11 +7,10 @@
|
||||
id: string;
|
||||
onselect?: (id: string) => void;
|
||||
disabled?: boolean;
|
||||
tooltipText?: string;
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
const { id, onselect, children, tooltipText, disabled = false }: SegmentProps = $props();
|
||||
const { id, onselect, children, disabled = false }: SegmentProps = $props();
|
||||
|
||||
const context = getContext<SegmentContext>('SegmentControl');
|
||||
const index = context.setIndex();
|
||||
@ -63,10 +61,6 @@
|
||||
}
|
||||
}
|
||||
}}
|
||||
use:tooltip={{
|
||||
text: tooltipText ? tooltipText : '',
|
||||
delay: 1000
|
||||
}}
|
||||
>
|
||||
<span class="text-12 label">
|
||||
{@render children()}
|
||||
|
@ -1,119 +0,0 @@
|
||||
export interface ToolTipOptions {
|
||||
text: string;
|
||||
noMaxWidth?: boolean;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
const defaultOptions: Partial<ToolTipOptions> = {
|
||||
delay: 1200,
|
||||
noMaxWidth: false
|
||||
};
|
||||
|
||||
export function tooltip(node: HTMLElement, optsOrString: ToolTipOptions | string | undefined) {
|
||||
// The tooltip element we are adding to the dom
|
||||
let tooltip: HTMLDivElement | undefined;
|
||||
|
||||
// Note that we use this both for delaying show, as well as delaying hide
|
||||
let timeoutId: any;
|
||||
|
||||
// Options
|
||||
let { text, delay, noMaxWidth } = defaultOptions;
|
||||
|
||||
// Most use cases only involve passing a string, so we allow either opts of
|
||||
// simple text.
|
||||
function setOpts(opts: ToolTipOptions | string | undefined) {
|
||||
if (typeof opts === 'string') {
|
||||
text = opts;
|
||||
} else if (opts) {
|
||||
({ text, delay, noMaxWidth } = opts || {});
|
||||
}
|
||||
if (tooltip && text) tooltip.innerText = text;
|
||||
}
|
||||
|
||||
setOpts(optsOrString);
|
||||
|
||||
function onPointerEnter() {
|
||||
// If tooltip is displayed we clear hide timeout
|
||||
if (tooltip && timeoutId) clearTimeout(timeoutId);
|
||||
// If no tooltip and no timeout id we set a show timeout
|
||||
else if (!tooltip && !timeoutId) timeoutId = setTimeout(() => show(), delay);
|
||||
}
|
||||
|
||||
function onPointerLeave() {
|
||||
// If tooltip shown when mouse out then we hide after delay
|
||||
if (tooltip) hide();
|
||||
// But if we mouse out before tooltip is shown, we cancel the show timer
|
||||
else if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function show() {
|
||||
if (!text || !node.isConnected) return;
|
||||
tooltip = document.createElement('div') as HTMLDivElement;
|
||||
// TODO: Can we co-locate tooltip.js & tooltip.postcss?
|
||||
tooltip.classList.add('tooltip', 'text-11'); // see tooltip.postcss
|
||||
if (noMaxWidth) tooltip.classList.add('no-max-width');
|
||||
tooltip.innerText = text;
|
||||
document.body.appendChild(tooltip);
|
||||
adjustPosition();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (tooltip) tooltip.remove();
|
||||
tooltip = undefined;
|
||||
timeoutId = undefined;
|
||||
}
|
||||
|
||||
function adjustPosition() {
|
||||
if (!tooltip) return;
|
||||
|
||||
// Dimensions and position of target element
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
const nodeHeight = nodeRect.height;
|
||||
const nodeWidth = nodeRect.width;
|
||||
const nodeLeft = nodeRect.left;
|
||||
const nodeTop = nodeRect.top;
|
||||
|
||||
// Padding
|
||||
const padding = 4;
|
||||
|
||||
// Window dimensions
|
||||
const windowHeight = window.innerHeight;
|
||||
const windowWidth = window.innerWidth;
|
||||
|
||||
const tooltipHeight = tooltip.offsetHeight;
|
||||
const tooltipWidth = tooltip.offsetWidth;
|
||||
|
||||
const showBelow = windowHeight > nodeTop + nodeHeight + tooltipHeight + padding;
|
||||
|
||||
// Note that we don't check if width of tooltip is wider than the window.
|
||||
|
||||
if (showBelow) {
|
||||
tooltip.style.top = `${nodeTop + nodeHeight + padding}px`;
|
||||
} else {
|
||||
tooltip.style.top = `${nodeTop - tooltipHeight - padding}px`;
|
||||
}
|
||||
|
||||
let leftPos = nodeLeft - (tooltipWidth - nodeWidth) / 2;
|
||||
if (leftPos < padding) leftPos = padding;
|
||||
if (leftPos + tooltipWidth > windowWidth) leftPos = windowWidth - tooltipWidth - padding;
|
||||
tooltip.style.left = `${leftPos}px`;
|
||||
}
|
||||
|
||||
node.addEventListener('pointerenter', onPointerEnter);
|
||||
node.addEventListener('pointerleave', onPointerLeave);
|
||||
|
||||
return {
|
||||
update(opts: ToolTipOptions | string | undefined) {
|
||||
setOpts(opts);
|
||||
},
|
||||
destroy() {
|
||||
tooltip?.remove();
|
||||
timeoutId && clearTimeout(timeoutId);
|
||||
node.removeEventListener('pointerenter', onPointerEnter);
|
||||
node.removeEventListener('pointerleave', onPointerLeave);
|
||||
}
|
||||
};
|
||||
}
|
52
packages/ui/src/lib/utils/transitions.ts
Normal file
52
packages/ui/src/lib/utils/transitions.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { pxToRem } from './pxToRem';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import { slide, type SlideParams, type TransitionConfig } from 'svelte/transition';
|
||||
|
||||
export function slideFade(node: Element, options: SlideParams): TransitionConfig {
|
||||
const slideTrans: TransitionConfig = slide(node, options);
|
||||
|
||||
return {
|
||||
...slideTrans,
|
||||
css: (t, u) =>
|
||||
`${slideTrans.css ? slideTrans.css(t, u) : ''}
|
||||
opacity:${t};`
|
||||
};
|
||||
}
|
||||
|
||||
export function flyScale(
|
||||
_: Element,
|
||||
params: {
|
||||
y?: number;
|
||||
x?: number;
|
||||
start?: number;
|
||||
duration?: number;
|
||||
position?: 'top' | 'bottom';
|
||||
} = {}
|
||||
): TransitionConfig {
|
||||
// Default values
|
||||
const DEFAULT_Y = -6;
|
||||
const DEFAULT_X = 0;
|
||||
const DEFAULT_SCALE_START = 0.94;
|
||||
const DEFAULT_DURATION = 200;
|
||||
const DEFAULT_POSITION = 'top';
|
||||
|
||||
// Extracting and using default values
|
||||
const y = params.y ?? DEFAULT_Y;
|
||||
const x = params.x ?? DEFAULT_X;
|
||||
const startScale = params.start ?? DEFAULT_SCALE_START;
|
||||
const duration = params.duration ?? DEFAULT_DURATION;
|
||||
const position = params.position ?? DEFAULT_POSITION;
|
||||
|
||||
return {
|
||||
duration,
|
||||
css: (t) => {
|
||||
const translateY = y * (1 - t);
|
||||
const translateX = x * (1 - t);
|
||||
const scale = startScale + t * (1 - startScale);
|
||||
|
||||
return `transform: translate3d(${pxToRem(translateX)}, ${pxToRem(position === 'top' ? -translateY : translateY)}, 0) scale(${scale});
|
||||
opacity: ${t};`;
|
||||
},
|
||||
easing: cubicOut
|
||||
};
|
||||
}
|
@ -13,7 +13,6 @@ export const BadgeStory: Story = {
|
||||
name: 'Badge',
|
||||
args: {
|
||||
label: '127',
|
||||
help: 'This is a badge',
|
||||
style: 'neutral',
|
||||
kind: 'solid'
|
||||
},
|
||||
|
@ -23,7 +23,6 @@ export const ButtonDefault: Story = {
|
||||
outline: false,
|
||||
dashed: false,
|
||||
solidBackground: false,
|
||||
help: '',
|
||||
helpShowDelay: 1200,
|
||||
id: 'button',
|
||||
tabindex: 0,
|
||||
@ -34,7 +33,7 @@ export const ButtonDefault: Story = {
|
||||
wide: false,
|
||||
grow: false,
|
||||
align: 'center',
|
||||
isDropdownChild: false,
|
||||
dropdownChild: false,
|
||||
onclick: () => {
|
||||
console.log('Button clicked');
|
||||
}
|
||||
@ -60,9 +59,16 @@ export const ButtonClickable: Story = {
|
||||
name: 'Not clickable + tooltip',
|
||||
args: {
|
||||
clickable: false,
|
||||
help: 'This button is not clickable',
|
||||
tooltip: 'This button is not clickable',
|
||||
tooltipAlign: 'start',
|
||||
onclick: () => {
|
||||
console.log('Button clicked');
|
||||
}
|
||||
},
|
||||
argTypes: {
|
||||
tooltipAlign: {
|
||||
control: 'select',
|
||||
options: ['start', 'center', 'end']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,8 @@ const meta = {
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const ModalStory: Story = {
|
||||
export const DefaultStory: Story = {
|
||||
name: 'Modal',
|
||||
args: {
|
||||
width: 'small',
|
||||
title: 'This is a fantastic modal :D'
|
||||
|
30
packages/ui/src/stories/tooltip/DemoTooltip.svelte
Normal file
30
packages/ui/src/stories/tooltip/DemoTooltip.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/Tooltip.svelte';
|
||||
|
||||
const props = $props();
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<p class="text-13 text">
|
||||
hello world! Here is a <Tooltip text={props.text}>
|
||||
<span class="tooltip-text">tooltip</span>
|
||||
</Tooltip> for you.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: var(--clr-text-1);
|
||||
}
|
||||
|
||||
.tooltip-text {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
}
|
||||
</style>
|
30
packages/ui/src/stories/tooltip/Tooltip.stories.ts
Normal file
30
packages/ui/src/stories/tooltip/Tooltip.stories.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import DemoTooltip from './DemoTooltip.svelte';
|
||||
import type { Meta, StoryObj } from '@storybook/svelte';
|
||||
|
||||
const meta = {
|
||||
title: 'Overlays / Tooltip',
|
||||
component: DemoTooltip
|
||||
} satisfies Meta<DemoTooltip>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const DefaultStory: Story = {
|
||||
name: 'Tooltip',
|
||||
args: {
|
||||
position: 'bottom',
|
||||
align: 'center',
|
||||
delay: 500,
|
||||
text: 'This is a fantastic tooltip :D'
|
||||
},
|
||||
argTypes: {
|
||||
position: {
|
||||
control: 'select',
|
||||
options: ['top', 'bottom']
|
||||
},
|
||||
align: {
|
||||
control: 'select',
|
||||
options: ['start', 'center', 'end']
|
||||
}
|
||||
}
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
background-color: var(--clr-core-ntrl-10);
|
||||
border-radius: var(--radius-s);
|
||||
border: 1px solid var(--clr-core-ntrl-30);
|
||||
color: var(--clr-core-ntrl-60);
|
||||
display: inline-block;
|
||||
padding: 6px 8px;
|
||||
z-index: var(--z-blocker);
|
||||
|
||||
max-width: 180px;
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
|
||||
opacity: 0;
|
||||
animation: showup-tooltip-animation 0.1s ease-out forwards;
|
||||
|
||||
&.no-max-width {
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showup-tooltip-animation {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-0.2rem) scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
@ -52,13 +52,13 @@
|
||||
--clr-core-succ-5: color(srgb 0.050980392156862744 0.14901960784313725 0.10196078431372549);
|
||||
--clr-core-succ-10: color(srgb 0.10980392156862745 0.25098039215686274 0.1843137254901961);
|
||||
--clr-core-succ-20: color(srgb 0.13333333333333333 0.3254901960784314 0.23529411764705882);
|
||||
--clr-core-succ-30: color(srgb 0.16470588235294117 0.49411764705882355 0.3411764705882353);
|
||||
--clr-core-succ-40: color(srgb 0.27450980392156865 0.6078431372549019 0.45098039215686275);
|
||||
--clr-core-succ-50: color(srgb 0.3058823529411765 0.6941176470588235 0.5098039215686274);
|
||||
--clr-core-succ-60: color(srgb 0.6313725490196078 0.807843137254902 0.7254901960784313);
|
||||
--clr-core-succ-70: color(srgb 0.792156862745098 0.9098039215686274 0.8549019607843137);
|
||||
--clr-core-succ-80: color(srgb 0.8392156862745098 0.9411764705882353 0.8941176470588236);
|
||||
--clr-core-succ-90: color(srgb 0.9098039215686274 0.9686274509803922 0.9411764705882353);
|
||||
--clr-core-succ-30: color(srgb 0.1568627450980392 0.4823529411764706 0.3333333333333333);
|
||||
--clr-core-succ-40: color(srgb 0.23529411764705882 0.6039215686274509 0.43529411764705883);
|
||||
--clr-core-succ-50: color(srgb 0.2901960784313726 0.7098039215686275 0.5098039215686274);
|
||||
--clr-core-succ-60: color(srgb 0.5725490196078431 0.8666666666666667 0.7294117647058823);
|
||||
--clr-core-succ-70: color(srgb 0.7450980392156863 0.9568627450980393 0.8549019607843137);
|
||||
--clr-core-succ-80: color(srgb 0.8156862745098039 0.9686274509803922 0.8980392156862745);
|
||||
--clr-core-succ-90: color(srgb 0.8980392156862745 0.9803921568627451 0.9411764705882353);
|
||||
--clr-core-succ-95: color(srgb 0.9647058823529412 0.9882352941176471 0.984313725490196);
|
||||
--clr-core-purp-5: color(srgb 0.1568627450980392 0.11372549019607843 0.26666666666666666);
|
||||
--clr-core-purp-10: color(srgb 0.24705882352941178 0.17254901960784313 0.40784313725490196);
|
||||
@ -235,6 +235,8 @@
|
||||
srgb 0.5294117647058824 0.6588235294117647 0.6039215686274509
|
||||
);
|
||||
--clr-diff-addition-line-bg: color(srgb 0.8784313725490196 0.984313725490196 0.9411764705882353);
|
||||
--clr-tooltip-border: var(--clr-core-ntrl-10);
|
||||
--clr-tooltip-bg: var(--clr-core-ntrl-10);
|
||||
--size-icon: 1rem;
|
||||
--size-tag: 1.375rem;
|
||||
--size-button: 1.75rem;
|
||||
@ -419,6 +421,8 @@
|
||||
--clr-diff-addition-line-bg: color(
|
||||
srgb 0.054901960784313725 0.1843137254901961 0.1450980392156863
|
||||
);
|
||||
--clr-tooltip-border: var(--clr-core-ntrl-30);
|
||||
--clr-tooltip-bg: var(--clr-core-ntrl-10);
|
||||
}
|
||||
|
||||
.bg-clr1 {
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
/* COMPONENTS */
|
||||
@import './components/diff.css';
|
||||
@import './components/tooltip.css';
|
||||
@import './components/text-input.css';
|
||||
@import './components/commit-lines.css';
|
||||
@import './components/draggable.css';
|
||||
|
@ -14,7 +14,7 @@ function clearFxPrefix(id) {
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
tokens: './src/lib/design-tokens.json',
|
||||
tokens: './src/lib/data/design-tokens.json',
|
||||
outDir: './src/styles/core',
|
||||
plugins: [
|
||||
css({
|
||||
|
Loading…
Reference in New Issue
Block a user