platform/packages/presentation/src/components/DrawingBoardToolbar.svelte
Chunosov f69d5ede0e
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Save drawing boards separately from document (#7257)
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
2024-12-04 02:56:59 +07:00

271 lines
6.7 KiB
Svelte

<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// 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 {
Button,
IconAdd,
IconDelete,
IconEdit,
IconMoreH,
IconRedo,
SelectPopup,
SelectPopupValueType,
eventToHTMLElement,
showPopup
} from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import IconEraser from './icons/Eraser.svelte'
import IconMove from './icons/Move.svelte'
import { DrawingTool } from '../drawing'
import presentation from '../plugin'
const dispatch = createEventDispatcher()
const maxColors = 8
const minColors = 0
const defaultColor = '#0000ff'
const defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#ffffff', '#000000']
const storageKey = {
color: 'drawingBoard.color',
colors: 'drawingBoard.colors',
penWidth: 'drawingBoard.penWidth',
eraserWidth: 'drawingBoard.eraserWidth'
}
export let tool: DrawingTool = 'pen'
export let penColor: string
export let penWidth: number
export let eraserWidth: number
export let placeInside = false
export let showPanTool = false
export let toolbar: HTMLDivElement | undefined
let colorSelector: HTMLInputElement
let penColors: string[] = defaultColors
function showMenu (ev: MouseEvent): void {
const items: SelectPopupValueType[] = []
if (penColors.length < maxColors) {
items.push({
id: 'add-color',
label: presentation.string.ColorAdd,
icon: IconAdd
})
}
if (penColors.length > minColors) {
items.push({
id: 'remove-color',
label: presentation.string.ColorRemove,
icon: IconDelete
})
}
items.push({
id: 'reset-colors',
label: presentation.string.ColorReset,
icon: IconRedo
})
showPopup(SelectPopup, { value: items }, eventToHTMLElement(ev), (id) => {
switch (id) {
case 'add-color': {
if (colorSelector !== undefined) {
colorSelector.value = penColor
colorSelector.showPicker()
}
break
}
case 'remove-color': {
penColors = penColors.filter((c: string) => c !== penColor)
localStorage.setItem(storageKey.colors, JSON.stringify(penColors))
selectColor(penColors[0])
break
}
case 'reset-colors': {
penColors = defaultColors
localStorage.removeItem(storageKey.colors)
selectColor(penColors[0])
break
}
case undefined: {
break
}
default: {
console.error('Unknown command id', id)
}
}
})
}
function addColorPreset (): void {
penColor = penColor.toLowerCase()
if (!penColors.includes(penColor)) {
penColors = [...penColors, penColor]
localStorage.setItem(storageKey.colors, JSON.stringify(penColors))
}
}
function selectColor (color: string): void {
penColor = color ?? defaultColor
localStorage.setItem(storageKey.color, penColor)
}
onMount(() => {
try {
const savedColors = localStorage.getItem(storageKey.colors)
penColors = savedColors !== null ? JSON.parse(savedColors.toLowerCase()) : defaultColors
} catch {
penColors = defaultColors
}
penColor = (localStorage.getItem(storageKey.color) ?? penColor ?? defaultColor).toLowerCase()
if (!penColors.includes(penColor)) {
penColor = penColors[0] ?? defaultColor
}
penWidth = parseInt(localStorage.getItem(storageKey.penWidth) ?? '6')
eraserWidth = parseInt(localStorage.getItem(storageKey.eraserWidth) ?? '50')
})
function updatePenWidth (): void {
localStorage.setItem(storageKey.penWidth, penWidth.toString())
}
function updateEraserWidth (): void {
localStorage.setItem(storageKey.eraserWidth, eraserWidth.toString())
}
</script>
<div class="toolbar" class:inside={placeInside} bind:this={toolbar}>
<Button
icon={IconDelete}
kind="icon"
on:click={() => {
tool = 'pen'
dispatch('clear')
}}
/>
<div class="divider buttons-divider" />
<Button
icon={IconEdit}
kind="icon"
selected={tool === 'pen'}
on:click={() => {
tool = 'pen'
}}
/>
<Button
icon={IconEraser}
kind="icon"
selected={tool === 'erase'}
on:click={() => {
tool = 'erase'
}}
/>
{#if showPanTool}
<Button
icon={IconMove}
kind="icon"
selected={tool === 'pan'}
on:click={() => {
tool = 'pan'
}}
/>
{/if}
<div class="divider buttons-divider" />
{#if tool === 'pen'}
<input
class="widthSelector"
type="range"
min={2}
max={18}
step={4}
bind:value={penWidth}
on:change={updatePenWidth}
/>
{:else}
<input
class="widthSelector"
type="range"
min={20}
max={110}
step={30}
bind:value={eraserWidth}
on:change={updateEraserWidth}
/>
{/if}
<div class="divider buttons-divider" />
{#each penColors as color}
<Button
kind="icon"
selected={penColor === color}
on:click={() => {
tool = 'pen'
selectColor(color)
}}
>
<div slot="content" class="colorIcon" style:background={color} />
</Button>
{/each}
<div>
<input
type="color"
class="colorSelector"
bind:this={colorSelector}
bind:value={penColor}
on:change={addColorPreset}
/>
<Button kind="icon" icon={IconMoreH} on:click={showMenu} />
</div>
</div>
<style lang="scss">
.toolbar {
position: absolute;
display: inline-flex;
align-items: center;
padding: 0.25rem;
bottom: 100%;
&.inside {
left: 0.5rem;
top: 0.5rem;
bottom: unset;
background-color: var(--theme-popup-header);
border-radius: var(--small-BorderRadius);
border: 1px solid var(--theme-popup-divider);
box-shadow: 0.05rem 0.05rem 0.25rem rgba(0, 0, 0, 0.2);
z-index: 1;
}
}
.colorIcon {
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
margin: -0.15rem;
box-shadow: 0px 0px 0.15rem 0px var(--theme-button-contrast-enabled);
}
.divider {
margin: 0 0.25rem;
}
.colorSelector {
position: absolute;
width: 0;
opacity: 0;
}
.widthSelector {
width: 80px;
}
</style>