mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 12:25:17 +00:00

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
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
271 lines
6.7 KiB
Svelte
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>
|