mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-07 16:30:49 +00:00
parent
9874c47cf2
commit
1f7f1b05cf
@ -15,20 +15,20 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { IntlString, Asset } from '@anticrm/platform'
|
import type { IntlString, Asset } from '@anticrm/platform'
|
||||||
import type { AnySvelteComponent } from '../types'
|
import type { AnySvelteComponent, TooltipAligment } from '../types'
|
||||||
|
|
||||||
import Icon from './Icon.svelte'
|
import Icon from './Icon.svelte'
|
||||||
import Tooltip from './Tooltip.svelte'
|
import Tooltip from './Tooltip.svelte'
|
||||||
|
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let direction: string = 'top'
|
export let direction: TooltipAligment | undefined
|
||||||
export let icon: Asset | AnySvelteComponent
|
export let icon: Asset | AnySvelteComponent
|
||||||
export let size: 'small' | 'medium' | 'large'
|
export let size: 'small' | 'medium' | 'large'
|
||||||
export let action: () => Promise<void>
|
export let action: () => Promise<void>
|
||||||
export let invisible: boolean = false
|
export let invisible: boolean = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Tooltip label={label} direction={direction}>
|
<Tooltip {label} {direction}>
|
||||||
<button class="button {size}" on:click|stopPropagation={action}>
|
<button class="button {size}" on:click|stopPropagation={action}>
|
||||||
<div class="icon {size}" class:invisible={invisible}>
|
<div class="icon {size}" class:invisible={invisible}>
|
||||||
{#if typeof (icon) === 'string'}
|
{#if typeof (icon) === 'string'}
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<svelte:component this={component} {...item.props}/>
|
<svelte:component this={component} {...item.props}/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="icon"><ActionIcon label={'Remove'} direction={'top'} icon={Close} size={'small'} action={async () => { item.selected = false }}/></div>
|
<div class="icon"><ActionIcon label={'Remove'} icon={Close} size={'small'} action={async () => { item.selected = false }}/></div>
|
||||||
</button>
|
</button>
|
||||||
{#if byTitle }
|
{#if byTitle }
|
||||||
<PopupItem bind:title={item.title} selectable bind:selected={item.selected}/>
|
<PopupItem bind:title={item.title} selectable bind:selected={item.selected}/>
|
||||||
|
@ -15,108 +15,24 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import Label from './Label.svelte'
|
import type { TooltipAligment } from '..'
|
||||||
|
import { showTooltip, closeTooltip } from '..'
|
||||||
|
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let direction: 'top' | 'bottom' | 'left' | 'right' = 'top'
|
export let direction: TooltipAligment | undefined
|
||||||
|
|
||||||
|
let triggerHTML: HTMLElement
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-center container">
|
<div
|
||||||
<div class="trigger"><slot/></div>
|
class="tooltip-trigger"
|
||||||
<div class="tooltip {direction}">
|
bind:this={triggerHTML}
|
||||||
<Label label={label}/>
|
on:mouseenter={(ev) => {
|
||||||
</div>
|
showTooltip(label, triggerHTML, direction)
|
||||||
|
}}
|
||||||
|
on:mouseleave={() => {
|
||||||
|
closeTooltip()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.trigger:hover + .tooltip {
|
|
||||||
opacity: 1;
|
|
||||||
&.top {
|
|
||||||
transform: translateY(-.625rem);
|
|
||||||
}
|
|
||||||
&.bottom {
|
|
||||||
transform: translateY(.625rem);
|
|
||||||
}
|
|
||||||
&.right {
|
|
||||||
transform: translateX(.625rem);
|
|
||||||
}
|
|
||||||
&.left {
|
|
||||||
transform: translateX(-.625rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
padding: .5rem;
|
|
||||||
color: var(--theme-caption-color);
|
|
||||||
background-color: var(--theme-tooltip-color);
|
|
||||||
border: 1px solid var(--theme-bg-accent-color);
|
|
||||||
border-radius: .5rem;
|
|
||||||
box-shadow: 0px .5rem 1.25rem rgba(0, 0, 0, .25);
|
|
||||||
opacity: 0;
|
|
||||||
transition: transform .3s ease, opacity .2s ease-in-out;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
text-align: center;
|
|
||||||
transition-delay: .2s;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
width: .875rem;
|
|
||||||
height: .875rem;
|
|
||||||
background-color: var(--theme-tooltip-color);
|
|
||||||
border: 1px solid var(--theme-bg-accent-color);
|
|
||||||
border-radius: 0 0 .25rem;
|
|
||||||
mask-image: linear-gradient(-45deg, rgba(0, 0, 0, 1) .655rem, rgba(0, 0, 0, 0) .656rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top::after, &.bottom::after {
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -.5rem;
|
|
||||||
}
|
|
||||||
&.top {
|
|
||||||
bottom: 100%;
|
|
||||||
box-shadow: 0px -.5rem 1.25rem rgba(0, 0, 0, .25);
|
|
||||||
&::after {
|
|
||||||
bottom: -.5rem;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.bottom {
|
|
||||||
top: 100%;
|
|
||||||
box-shadow: 0px -.5rem 1.25rem rgba(0, 0, 0, .25);
|
|
||||||
&::after {
|
|
||||||
top: -.5rem;
|
|
||||||
transform: rotate(-135deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right::after, &.left::after {
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -.5rem;
|
|
||||||
}
|
|
||||||
&.right {
|
|
||||||
left: 100%;
|
|
||||||
box-shadow: -.5rem 0px 1.25rem rgba(0, 0, 0, .25);
|
|
||||||
&::after {
|
|
||||||
left: -.4rem;
|
|
||||||
transform: rotate(135deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.left {
|
|
||||||
right: 100%;
|
|
||||||
box-shadow: .5rem 0px 1.25rem rgba(0, 0, 0, .25);
|
|
||||||
&::after {
|
|
||||||
right: -.4rem;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
146
packages/ui/src/components/TooltipInstance.svelte
Normal file
146
packages/ui/src/components/TooltipInstance.svelte
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020 Anticrm Platform Contributors.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { tooltipstore as tooltip } from '..'
|
||||||
|
import type { TooltipAligment } from '..'
|
||||||
|
import Label from './Label.svelte'
|
||||||
|
|
||||||
|
let tooltipHTML: HTMLElement
|
||||||
|
let dir: TooltipAligment
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if ($tooltip.label && tooltipHTML) {
|
||||||
|
if ($tooltip.element) {
|
||||||
|
const rect = $tooltip.element.getBoundingClientRect()
|
||||||
|
const doc = document.body.getBoundingClientRect()
|
||||||
|
|
||||||
|
if (!$tooltip.direction) {
|
||||||
|
if (rect.right < doc.width / 5) dir = 'right'
|
||||||
|
else if (rect.left > doc.width - doc.width / 5) dir = 'left'
|
||||||
|
else if (rect.top < tooltipHTML.clientHeight) dir = 'bottom'
|
||||||
|
else dir = 'top'
|
||||||
|
} else dir = $tooltip.direction
|
||||||
|
|
||||||
|
if (dir === 'right') {
|
||||||
|
tooltipHTML.style.top = rect.y + rect.height / 2 + 'px'
|
||||||
|
tooltipHTML.style.left = `calc(${rect.right}px + .75rem)`
|
||||||
|
tooltipHTML.style.transform = 'translateY(-50%)'
|
||||||
|
} else if (dir === 'left') {
|
||||||
|
tooltipHTML.style.top = rect.y + rect.height / 2 + 'px'
|
||||||
|
tooltipHTML.style.right = `calc(${doc.width - rect.x}px + .75rem)`
|
||||||
|
tooltipHTML.style.transform = 'translateY(-50%)'
|
||||||
|
} else if (dir === 'bottom') {
|
||||||
|
tooltipHTML.style.top = `calc(${rect.bottom}px + .5rem)`
|
||||||
|
tooltipHTML.style.left = rect.x + rect.width / 2 + 'px'
|
||||||
|
tooltipHTML.style.transform = 'translateX(-50%)'
|
||||||
|
} else if (dir === 'top') {
|
||||||
|
tooltipHTML.style.bottom = `calc(${doc.height - rect.y}px + .75rem)`
|
||||||
|
tooltipHTML.style.left = rect.x + rect.width / 2 + 'px'
|
||||||
|
tooltipHTML.style.transform = 'translateX(-50%)'
|
||||||
|
}
|
||||||
|
tooltipHTML.classList.remove('no-arrow')
|
||||||
|
} else {
|
||||||
|
tooltipHTML.style.top = '50%'
|
||||||
|
tooltipHTML.style.left = '50%'
|
||||||
|
tooltipHTML.style.width = 'min-content'
|
||||||
|
tooltipHTML.style.height = 'min-content'
|
||||||
|
tooltipHTML.style.transform = 'translate(-50%, -50%)'
|
||||||
|
tooltipHTML.classList.add('no-arrow')
|
||||||
|
}
|
||||||
|
tooltipHTML.style.visibility = 'visible'
|
||||||
|
} else if (tooltipHTML) tooltipHTML.style.visibility = 'hidden'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $tooltip.label}
|
||||||
|
<div class="tooltip {dir}" bind:this={tooltipHTML}>
|
||||||
|
<Label label={$tooltip.label} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tooltip {
|
||||||
|
position: fixed;
|
||||||
|
padding: .5rem;
|
||||||
|
color: var(--theme-caption-color);
|
||||||
|
background-color: var(--theme-tooltip-color);
|
||||||
|
border: 1px solid var(--theme-bg-accent-color);
|
||||||
|
border-radius: .5rem;
|
||||||
|
user-select: none;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: .875rem;
|
||||||
|
height: .875rem;
|
||||||
|
background-color: var(--theme-tooltip-color);
|
||||||
|
border: 1px solid var(--theme-bg-accent-color);
|
||||||
|
border-radius: 0 0 3px;
|
||||||
|
clip-path: polygon(100% 25%, 100% 100%, 25% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top::after,
|
||||||
|
&.bottom::after {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -.5rem;
|
||||||
|
}
|
||||||
|
&.top {
|
||||||
|
bottom: 100%;
|
||||||
|
box-shadow: 0px 8px 20px rgba(0, 0, 0, .35);
|
||||||
|
&::after {
|
||||||
|
bottom: -.3125rem;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.bottom {
|
||||||
|
top: 100%;
|
||||||
|
box-shadow: 0px -8px 20px rgba(0, 0, 0, .35);
|
||||||
|
&::after {
|
||||||
|
top: -.3125rem;
|
||||||
|
transform: rotate(-135deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right::after,
|
||||||
|
&.left::after {
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -.5rem;
|
||||||
|
}
|
||||||
|
&.right {
|
||||||
|
left: 100%;
|
||||||
|
box-shadow: -8px 0px 20px rgba(0, 0, 0, .35);
|
||||||
|
&::after {
|
||||||
|
left: -.3125rem;
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.left {
|
||||||
|
right: 100%;
|
||||||
|
box-shadow: 8px 0px 20px rgba(0, 0, 0, .35);
|
||||||
|
&::after {
|
||||||
|
right: -.3125rem;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.no-arrow {
|
||||||
|
box-shadow: 0px 0px 20px rgba(0, 0, 0, .75);
|
||||||
|
&::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,10 +14,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { SvelteComponent } from 'svelte'
|
import { SvelteComponent } from 'svelte'
|
||||||
|
import type { AnySvelteComponent, AnyComponent, PopupAlignment, LabelAndProps, TooltipAligment } from './types'
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
|
||||||
import Root from './components/internal/Root.svelte'
|
import Root from './components/internal/Root.svelte'
|
||||||
|
|
||||||
export type { AnyComponent, AnySvelteComponent, Action } from './types'
|
export type { AnyComponent, AnySvelteComponent, Action, LabelAndProps, TooltipAligment } from './types'
|
||||||
// export { applicationShortcutKey } from './utils'
|
// export { applicationShortcutKey } from './utils'
|
||||||
export { getCurrentLocation, navigate, location } from './location'
|
export { getCurrentLocation, navigate, location } from './location'
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ export { default as Toggle } from './components/Toggle.svelte'
|
|||||||
export { default as Dialog } from './components/Dialog.svelte'
|
export { default as Dialog } from './components/Dialog.svelte'
|
||||||
export { default as ToggleWithLabel } from './components/ToggleWithLabel.svelte'
|
export { default as ToggleWithLabel } from './components/ToggleWithLabel.svelte'
|
||||||
export { default as Tooltip } from './components/Tooltip.svelte'
|
export { default as Tooltip } from './components/Tooltip.svelte'
|
||||||
|
export { default as TooltipInstance } from './components/TooltipInstance.svelte'
|
||||||
export { default as CheckBox } from './components/CheckBox.svelte'
|
export { default as CheckBox } from './components/CheckBox.svelte'
|
||||||
export { default as Progress } from './components/Progress.svelte'
|
export { default as Progress } from './components/Progress.svelte'
|
||||||
export { default as Tabs } from './components/Tabs.svelte'
|
export { default as Tabs } from './components/Tabs.svelte'
|
||||||
@ -66,7 +69,6 @@ export { default as IconThread } from './components/icons/Thread.svelte'
|
|||||||
|
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
|
||||||
import type { AnySvelteComponent, AnyComponent, PopupAlignment } from './types'
|
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
export function createApp (target: HTMLElement): SvelteComponent {
|
export function createApp (target: HTMLElement): SvelteComponent {
|
||||||
@ -100,3 +102,17 @@ export function closePopup (): void {
|
|||||||
return popups
|
return popups
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const tooltipstore = writable<LabelAndProps>({
|
||||||
|
label: undefined,
|
||||||
|
element: undefined,
|
||||||
|
direction: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
export function showTooltip (label: IntlString, element: HTMLElement, direction?: TooltipAligment): void {
|
||||||
|
tooltipstore.set({ label: label, element: element, direction: direction })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeTooltip (): void {
|
||||||
|
tooltipstore.set({ label: undefined, element: undefined, direction: undefined })
|
||||||
|
}
|
||||||
|
@ -55,3 +55,11 @@ export interface Tab {
|
|||||||
export type TabModel = Tab[]
|
export type TabModel = Tab[]
|
||||||
|
|
||||||
export type PopupAlignment = HTMLElement | 'right' | 'float'
|
export type PopupAlignment = HTMLElement | 'right' | 'float'
|
||||||
|
|
||||||
|
export type TooltipAligment = 'top' | 'bottom' | 'left' | 'right'
|
||||||
|
|
||||||
|
export interface LabelAndProps {
|
||||||
|
label: IntlString | undefined
|
||||||
|
element: HTMLElement | undefined
|
||||||
|
direction?: TooltipAligment
|
||||||
|
}
|
||||||
|
@ -48,10 +48,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if !thread}
|
{#if !thread}
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div class="tool"><ActionIcon icon={IconMoreH} size={'medium'} direction={'left'}/></div>
|
<div class="tool"><ActionIcon icon={IconMoreH} size={'medium'}/></div>
|
||||||
<div class="tool"><ActionIcon icon={Bookmark} size={'medium'} direction={'left'}/></div>
|
<div class="tool"><ActionIcon icon={Bookmark} size={'medium'}/></div>
|
||||||
<div class="tool"><ActionIcon icon={Share} size={'medium'} direction={'left'}/></div>
|
<div class="tool"><ActionIcon icon={Share} size={'medium'}/></div>
|
||||||
<div class="tool"><ActionIcon icon={Emoji} size={'medium'} direction={'left'}/></div>
|
<div class="tool"><ActionIcon icon={Emoji} size={'medium'}/></div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,16 +24,14 @@
|
|||||||
export let notify: boolean
|
export let notify: boolean
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button class="app" class:selected={selected} on:click={action}>
|
<Tooltip {label}>
|
||||||
<Tooltip label={label} direction="right">
|
<button class="app" class:selected={selected} on:click={action}>
|
||||||
<div class="icon-container" class:noty={notify}>
|
<div class="icon-container" class:noty={notify}>
|
||||||
<Icon icon={icon} size={'large'}/>
|
<Icon icon={icon} size={'large'}/>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
{#if notify}<div class="marker"/>{/if}
|
||||||
{#if notify}
|
</button>
|
||||||
<div class="marker"/>
|
</Tooltip>
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.app {
|
.app {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
import SpaceHeader from './SpaceHeader.svelte'
|
import SpaceHeader from './SpaceHeader.svelte'
|
||||||
import SpaceView from './SpaceView.svelte'
|
import SpaceView from './SpaceView.svelte'
|
||||||
|
|
||||||
import { AnyComponent, location, Popup, showPopup } from '@anticrm/ui'
|
import { AnyComponent, location, Popup, showPopup, TooltipInstance } from '@anticrm/ui'
|
||||||
import core from '@anticrm/core'
|
import core from '@anticrm/core'
|
||||||
import CreateUser from './CreateUser.svelte'
|
import CreateUser from './CreateUser.svelte'
|
||||||
|
|
||||||
@ -92,6 +92,7 @@
|
|||||||
<!-- <div class="aside"><Chat thread/></div> -->
|
<!-- <div class="aside"><Chat thread/></div> -->
|
||||||
</div>
|
</div>
|
||||||
<Popup />
|
<Popup />
|
||||||
|
<TooltipInstance />
|
||||||
{:else}
|
{:else}
|
||||||
No client
|
No client
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
Reference in New Issue
Block a user