mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
UBER-376 Implement new style for the tooltips (#3418)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
95ffbd6d33
commit
626d36db7d
@ -1315,7 +1315,7 @@ export function createModel (builder: Builder): void {
|
||||
group: 'create'
|
||||
}
|
||||
},
|
||||
tracker.action.NewSubIssue
|
||||
tracker.action.NewIssue
|
||||
)
|
||||
|
||||
createAction(
|
||||
|
@ -154,6 +154,10 @@
|
||||
--theme-calendar-holiday-bgcolor: rgba(235, 87, 87, .1);
|
||||
--theme-calendar-weekend-bgcolor: rgba(242, 153, 74, .05);
|
||||
|
||||
--theme-tooltip-color: rgba(255, 255, 255, .8);
|
||||
--theme-tooltip-bg: #353347;
|
||||
--theme-tooltip-key-bg: rgba(255, 255, 255, .08);
|
||||
|
||||
--theme-inbox-notify: #F47758;
|
||||
--theme-inbox-people-notify: #2B5190;
|
||||
--theme-inbox-activity-bgcolor: #1A1A28;
|
||||
@ -347,6 +351,10 @@
|
||||
--theme-calendar-holiday-bgcolor: rgba(235, 87, 87, .1);
|
||||
--theme-calendar-weekend-bgcolor: rgba(242, 153, 74, .1);
|
||||
|
||||
--theme-tooltip-color: #FFF;
|
||||
--theme-tooltip-bg: #444248;
|
||||
--theme-tooltip-key-bg: rgba(255, 255, 255, .08);
|
||||
|
||||
--theme-inbox-notify: #F47758;
|
||||
--theme-inbox-people-notify: #2B5190;
|
||||
--theme-inbox-activity-bgcolor: #fff;
|
||||
|
@ -19,6 +19,7 @@
|
||||
import type { TooltipAlignment } from '../types'
|
||||
import Component from './Component.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import { capitalizeFirstLetter, formatKey } from '../utils'
|
||||
|
||||
let tooltipHTML: HTMLElement
|
||||
let nubHTML: HTMLElement
|
||||
@ -236,6 +237,25 @@
|
||||
{:else if $tooltip.label && $tooltip.kind !== 'submenu'}
|
||||
<div class="tooltip {dir ?? ''}" bind:this={tooltipHTML}>
|
||||
<Label label={$tooltip.label} params={$tooltip.props ?? {}} />
|
||||
{#if $tooltip.keys !== undefined}
|
||||
<div class="keys">
|
||||
{#each $tooltip.keys as key, i}
|
||||
{#if i !== 0}
|
||||
<div class="mr-1 ml-1">/</div>
|
||||
{/if}
|
||||
{#each formatKey(key) as k, jj}
|
||||
<div class="key">
|
||||
{#each k as kk, j}
|
||||
{#if j !== 0}
|
||||
+
|
||||
{/if}
|
||||
{capitalizeFirstLetter(kk.trim())}
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $tooltip.kind === 'submenu'}
|
||||
<div
|
||||
@ -361,25 +381,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
.keys {
|
||||
margin-left: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.125rem;
|
||||
}
|
||||
|
||||
.key {
|
||||
border-radius: 0.125rem;
|
||||
font-size: 0.75rem;
|
||||
min-width: 1.5rem;
|
||||
padding: 0.25rem;
|
||||
background-color: var(--theme-tooltip-key-bg);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
padding: 0.5rem 0.75rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
color: var(--theme-content-color);
|
||||
background-color: var(--theme-popup-color);
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-tooltip-color);
|
||||
background-color: var(--theme-tooltip-bg);
|
||||
border: 1px solid var(--theme-popup-divider);
|
||||
border-radius: 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: var(--theme-popup-shadow);
|
||||
user-select: none;
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 7px;
|
||||
}
|
||||
&::before {
|
||||
background-color: var(--theme-popup-color);
|
||||
clip-path: url('#nub-bg');
|
||||
@ -390,55 +421,6 @@
|
||||
clip-path: url('#nub-border');
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.top::after,
|
||||
&.bottom::after,
|
||||
&.top::before,
|
||||
&.bottom::before {
|
||||
left: 50%;
|
||||
margin-left: -9px;
|
||||
}
|
||||
&.top {
|
||||
bottom: 100%;
|
||||
&::after,
|
||||
&::before {
|
||||
bottom: -6px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
&.bottom {
|
||||
top: 100%;
|
||||
&::after,
|
||||
&::before {
|
||||
top: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
&.right::after,
|
||||
&.left::after,
|
||||
&.right::before,
|
||||
&.left::before {
|
||||
top: 50%;
|
||||
margin-top: -9px;
|
||||
}
|
||||
&.right {
|
||||
left: 100%;
|
||||
&::after,
|
||||
&::before {
|
||||
transform-origin: right top;
|
||||
left: -24px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
&.left {
|
||||
right: 100%;
|
||||
&::after,
|
||||
&::before {
|
||||
transform-origin: left top;
|
||||
right: -24px;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.no-arrow {
|
||||
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.75);
|
||||
|
@ -27,6 +27,7 @@
|
||||
TCellStyle,
|
||||
ICell
|
||||
} from './internal/DateUtils'
|
||||
import { capitalizeFirstLetter } from '../../utils'
|
||||
|
||||
export let currentDate: Date | null
|
||||
export let mondayStart: boolean = true
|
||||
@ -42,7 +43,6 @@
|
||||
if (areDatesEqual(today, new Date(viewDate.getFullYear(), viewDate.getMonth(), n))) return true
|
||||
return false
|
||||
}
|
||||
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
let days: Array<ICell> = []
|
||||
const getDateStyle = (date: Date): TCellStyle => {
|
||||
|
@ -18,6 +18,7 @@
|
||||
import IconNavNext from '../icons/NavNext.svelte'
|
||||
import Icon from '../Icon.svelte'
|
||||
import { firstDay, day, getWeekDayName, areDatesEqual, getMonthName, weekday, isWeekend } from './internal/DateUtils'
|
||||
import { capitalizeFirstLetter } from '../../utils'
|
||||
|
||||
export let currentDate: Date | null
|
||||
export let viewDate: Date
|
||||
@ -32,7 +33,6 @@
|
||||
$: firstDayOfCurrentMonth = firstDay(viewDate, mondayStart)
|
||||
let monthYear: string
|
||||
const today: Date = new Date(Date.now())
|
||||
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
afterUpdate(() => {
|
||||
monthYear = capitalizeFirstLetter(getMonthName(viewDate)) + ' ' + viewDate.getFullYear()
|
||||
|
@ -10,6 +10,7 @@ const emptyTooltip: LabelAndProps = {
|
||||
props: undefined,
|
||||
anchor: undefined,
|
||||
onUpdate: undefined,
|
||||
keys: undefined,
|
||||
kind: 'tooltip'
|
||||
}
|
||||
let storedValue: LabelAndProps = emptyTooltip
|
||||
@ -27,10 +28,30 @@ export function tooltip (node: HTMLElement, options?: LabelAndProps): any {
|
||||
if (opt.kind !== 'submenu') {
|
||||
clearTimeout(toHandler)
|
||||
toHandler = setTimeout(() => {
|
||||
showTooltip(opt.label, node, opt.direction, opt.component, opt.props, opt.anchor, opt.onUpdate, opt.kind)
|
||||
showTooltip(
|
||||
opt.label,
|
||||
node,
|
||||
opt.direction,
|
||||
opt.component,
|
||||
opt.props,
|
||||
opt.anchor,
|
||||
opt.onUpdate,
|
||||
opt.kind,
|
||||
opt.keys
|
||||
)
|
||||
}, 250)
|
||||
} else {
|
||||
showTooltip(opt.label, node, opt.direction, opt.component, opt.props, opt.anchor, opt.onUpdate, opt.kind)
|
||||
showTooltip(
|
||||
opt.label,
|
||||
node,
|
||||
opt.direction,
|
||||
opt.component,
|
||||
opt.props,
|
||||
opt.anchor,
|
||||
opt.onUpdate,
|
||||
opt.kind,
|
||||
opt.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,7 +66,17 @@ export function tooltip (node: HTMLElement, options?: LabelAndProps): any {
|
||||
if (node !== storedValue.element) return
|
||||
const shown = !!(storedValue.label !== undefined || storedValue.component !== undefined)
|
||||
if (shown) {
|
||||
showTooltip(opt.label, node, opt.direction, opt.component, opt.props, opt.anchor, opt.onUpdate, opt.kind)
|
||||
showTooltip(
|
||||
opt.label,
|
||||
node,
|
||||
opt.direction,
|
||||
opt.component,
|
||||
opt.props,
|
||||
opt.anchor,
|
||||
opt.onUpdate,
|
||||
opt.kind,
|
||||
opt.keys
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@ -64,7 +95,8 @@ export function showTooltip (
|
||||
props?: any,
|
||||
anchor?: HTMLElement,
|
||||
onUpdate?: (result: any) => void,
|
||||
kind?: 'tooltip' | 'submenu' | 'popup'
|
||||
kind?: 'tooltip' | 'submenu' | 'popup',
|
||||
keys?: string[]
|
||||
): void {
|
||||
storedValue = {
|
||||
label,
|
||||
@ -74,7 +106,8 @@ export function showTooltip (
|
||||
props,
|
||||
anchor,
|
||||
onUpdate,
|
||||
kind
|
||||
kind,
|
||||
keys
|
||||
}
|
||||
tooltipstore.update((old) => {
|
||||
if (old.component === storedValue.component) {
|
||||
|
@ -238,6 +238,7 @@ export interface LabelAndProps {
|
||||
anchor?: HTMLElement
|
||||
onUpdate?: (result: any) => void
|
||||
kind?: 'tooltip' | 'submenu' | 'popup'
|
||||
keys?: string[]
|
||||
}
|
||||
|
||||
export interface ListItem {
|
||||
|
@ -197,3 +197,36 @@ export interface IModeSelector {
|
||||
config: Array<[string, IntlString, object]>
|
||||
onChange: (_mode: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function capitalizeFirstLetter (str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
const isMac = /Macintosh/i.test(navigator.userAgent)
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function formatKey (key: string): string[][] {
|
||||
const thens = key.split('->')
|
||||
const result: string[][] = []
|
||||
for (const r of thens) {
|
||||
result.push(
|
||||
r.split('+').map((it) =>
|
||||
it
|
||||
.replace(/key/g, '')
|
||||
.replace(/Meta|meta/g, isMac ? '⌘' : 'Ctrl')
|
||||
.replace(/ArrowUp/g, '↑')
|
||||
.replace(/ArrowDown/g, '↓')
|
||||
.replace(/ArrowLeft/g, '←')
|
||||
.replace(/ArrowRight/g, '→')
|
||||
.replace(/Backspace/g, '⌫')
|
||||
.toLocaleLowerCase()
|
||||
)
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -14,8 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { MultipleDraftController } from '@hcengineering/presentation'
|
||||
import { MultipleDraftController, getClient } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, showPopup } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../plugin'
|
||||
import CreateIssue from './CreateIssue.svelte'
|
||||
@ -38,18 +39,31 @@
|
||||
closed = true
|
||||
})
|
||||
}
|
||||
|
||||
$: label = draftExists || !closed ? tracker.string.ResumeDraft : tracker.string.NewIssue
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let keys: string[] | undefined = undefined
|
||||
|
||||
client.findOne(view.class.Action, { _id: tracker.action.NewIssue }).then((p) => (keys = p?.keyBinding))
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={draftExists || !closed ? tracker.string.ResumeDraft : tracker.string.NewIssue}
|
||||
{label}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
width={'100%'}
|
||||
size={'large'}
|
||||
on:click={newIssue}
|
||||
id="new-issue"
|
||||
showTooltip={{
|
||||
direction: 'bottom',
|
||||
label,
|
||||
keys
|
||||
}}
|
||||
>
|
||||
<div slot="content" class="draft-circle-container">
|
||||
{#if draftExists}
|
||||
|
@ -481,6 +481,7 @@ export default plugin(trackerId, {
|
||||
MoveToProject: '' as Ref<Action>,
|
||||
Duplicate: '' as Ref<Action>,
|
||||
Relations: '' as Ref<Action>,
|
||||
NewIssue: '' as Ref<Action>,
|
||||
NewSubIssue: '' as Ref<Action>,
|
||||
EditWorkflowStatuses: '' as Ref<Action>,
|
||||
EditProject: '' as Ref<Action>,
|
||||
|
@ -25,7 +25,9 @@
|
||||
Label,
|
||||
EditWithIcon,
|
||||
IconSearch,
|
||||
deviceOptionsStore
|
||||
deviceOptionsStore,
|
||||
capitalizeFirstLetter,
|
||||
formatKey
|
||||
} from '@hcengineering/ui'
|
||||
import { Action, ViewContext } from '@hcengineering/view'
|
||||
import { filterActions, getSelection } from '../actions'
|
||||
@ -167,30 +169,7 @@
|
||||
handleSelection(key, selection)
|
||||
}
|
||||
}
|
||||
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
const isMac = /Macintosh/i.test(navigator.userAgent)
|
||||
|
||||
function formatKey (key: string): string[][] {
|
||||
const thens = key.split('->')
|
||||
const result: string[][] = []
|
||||
for (const r of thens) {
|
||||
result.push(
|
||||
r.split('+').map((it) =>
|
||||
it
|
||||
.replaceAll('key', '')
|
||||
.replaceAll(/Meta|meta/g, isMac ? '⌘' : 'Ctrl')
|
||||
.replaceAll('ArrowUp', '↑')
|
||||
.replaceAll('ArrowDown', '↓')
|
||||
.replaceAll('ArrowLeft', '←')
|
||||
.replaceAll('ArrowRight', '→')
|
||||
.replaceAll('Backspace', '⌫')
|
||||
.toLocaleLowerCase()
|
||||
)
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
||||
icon={view.icon.ViewButton}
|
||||
label={view.string.View}
|
||||
{kind}
|
||||
showTooltip={{ label: view.string.CustomizeView }}
|
||||
showTooltip={{ label: view.string.CustomizeView, direction: 'bottom' }}
|
||||
bind:input={btn}
|
||||
on:click={clickHandler}
|
||||
/>
|
||||
|
@ -37,7 +37,7 @@
|
||||
icon={view.icon.Configure}
|
||||
label={view.string.Show}
|
||||
{kind}
|
||||
showTooltip={{ label: view.string.CustomizeView }}
|
||||
showTooltip={{ label: view.string.CustomizeView, direction: 'bottom' }}
|
||||
bind:input={btn}
|
||||
on:click={clickHandler}
|
||||
/>
|
||||
|
@ -3,7 +3,9 @@
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import {
|
||||
Button,
|
||||
capitalizeFirstLetter,
|
||||
closePopup,
|
||||
formatKey,
|
||||
getCurrentResolvedLocation,
|
||||
Icon,
|
||||
IconArrowLeft,
|
||||
@ -34,28 +36,6 @@
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
function formatKey (key: string): string[][] {
|
||||
const thens = key.split('->')
|
||||
const result: string[][] = []
|
||||
for (const r of thens) {
|
||||
result.push(
|
||||
r.split('+').map((it) =>
|
||||
it
|
||||
.replaceAll('key', '')
|
||||
.replaceAll(/Meta|meta/g, isMac ? '⌘' : 'Ctrl')
|
||||
.replaceAll('ArrowUp', '↑')
|
||||
.replaceAll('ArrowDown', '↓')
|
||||
.replaceAll('ArrowLeft', '←')
|
||||
.replaceAll('ArrowRight', '→')
|
||||
.replaceAll('Backspace', '⌫')
|
||||
)
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function getActions () {
|
||||
categories = await getClient().findAll(view.class.ActionCategory, [])
|
||||
const rawActions = await client.findAll(view.class.Action, [])
|
||||
@ -82,8 +62,6 @@
|
||||
}
|
||||
getActions()
|
||||
|
||||
const isMac = /Macintosh/i.test(navigator.userAgent)
|
||||
|
||||
const cards = [
|
||||
{
|
||||
icon: DocumentationIcon,
|
||||
|
Loading…
Reference in New Issue
Block a user