@lvl99dotcom/paintcanvas 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +131 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/lib/brushes/BrushTipCache/index.d.ts +32 -0
- package/dist/lib/brushes/BrushTipCache/index.js +60 -0
- package/dist/lib/brushes/StrokeEngine/index.d.ts +171 -0
- package/dist/lib/brushes/StrokeEngine/index.js +324 -0
- package/dist/lib/brushes/StrokePath/index.d.ts +53 -0
- package/dist/lib/brushes/StrokePath/index.js +161 -0
- package/dist/lib/brushes/StrokeSampler/index.d.ts +55 -0
- package/dist/lib/brushes/StrokeSampler/index.js +75 -0
- package/dist/lib/brushes/authors/index.d.ts +55 -0
- package/dist/lib/brushes/authors/index.js +60 -0
- package/dist/lib/brushes/configRanges/index.d.ts +52 -0
- package/dist/lib/brushes/configRanges/index.js +206 -0
- package/dist/lib/brushes/index.d.ts +5 -0
- package/dist/lib/brushes/index.js +19 -0
- package/dist/lib/brushes/params/index.d.ts +108 -0
- package/dist/lib/brushes/params/index.js +139 -0
- package/dist/lib/brushes/presetFile/index.d.ts +32 -0
- package/dist/lib/brushes/presetFile/index.js +55 -0
- package/dist/lib/brushes/presets/drawing/brush-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/felt-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/index.d.ts +13 -0
- package/dist/lib/brushes/presets/drawing/index.js +88 -0
- package/dist/lib/brushes/presets/drawing/pencil-2b.js +3 -0
- package/dist/lib/brushes/presets/drawing/pencil-hb.js +3 -0
- package/dist/lib/brushes/presets/painting/grass.js +3 -0
- package/dist/lib/brushes/presets/painting/hair.js +3 -0
- package/dist/lib/brushes/presets/painting/index.d.ts +18 -0
- package/dist/lib/brushes/presets/painting/index.js +118 -0
- package/dist/lib/brushes/presets/painting/splatter.js +3 -0
- package/dist/lib/brushes/presets/painting/textured-paint.js +3 -0
- package/dist/lib/brushes/presets/painting/watery-paint.js +3 -0
- package/dist/lib/brushes/presets/round/index.d.ts +9 -0
- package/dist/lib/brushes/presets/round/index.js +44 -0
- package/dist/lib/brushes/presets/round/round.js +3 -0
- package/dist/lib/brushes/presets/round/soft-round.js +3 -0
- package/dist/lib/brushes/presets/sized/index.d.ts +20 -0
- package/dist/lib/brushes/presets/sized/index.js +30 -0
- package/dist/lib/brushes/presets/sketching/index.d.ts +14 -0
- package/dist/lib/brushes/presets/sketching/index.js +107 -0
- package/dist/lib/brushes/presets/square/index.d.ts +7 -0
- package/dist/lib/brushes/presets/square/index.js +22 -0
- package/dist/lib/brushes/presets/square/square.js +3 -0
- package/dist/lib/brushes/shapeImage/index.d.ts +40 -0
- package/dist/lib/brushes/shapeImage/index.js +61 -0
- package/dist/lib/brushes/types/index.d.ts +243 -0
- package/dist/lib/brushes/types/index.js +20 -0
- package/dist/lib/classes/BackgroundWork/index.d.ts +55 -0
- package/dist/lib/classes/BackgroundWork/index.js +97 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +91 -0
- package/dist/lib/classes/BrushSettings/index.js +152 -0
- package/dist/lib/classes/CustomPresets/index.d.ts +53 -0
- package/dist/lib/classes/CustomPresets/index.js +71 -0
- package/dist/lib/classes/ExternalSheets/index.d.ts +64 -0
- package/dist/lib/classes/ExternalSheets/index.js +112 -0
- package/dist/lib/classes/GestureTracker/index.d.ts +62 -0
- package/dist/lib/classes/GestureTracker/index.js +122 -0
- package/dist/lib/classes/History/index.d.ts +83 -0
- package/dist/lib/classes/History/index.js +126 -0
- package/dist/lib/classes/HistoryGuard/index.d.ts +58 -0
- package/dist/lib/classes/HistoryGuard/index.js +97 -0
- package/dist/lib/classes/InputSettings/index.d.ts +74 -0
- package/dist/lib/classes/InputSettings/index.js +101 -0
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +48 -0
- package/dist/lib/classes/KeyboardShortcuts/index.js +137 -0
- package/dist/lib/classes/Layer/index.d.ts +86 -0
- package/dist/lib/classes/Layer/index.js +111 -0
- package/dist/lib/classes/LayerAutosave/index.d.ts +180 -0
- package/dist/lib/classes/LayerAutosave/index.js +346 -0
- package/dist/lib/classes/LayerStack/index.d.ts +177 -0
- package/dist/lib/classes/LayerStack/index.js +394 -0
- package/dist/lib/classes/PaintCanvas/index.d.ts +537 -0
- package/dist/lib/classes/PaintCanvas/index.js +965 -0
- package/dist/lib/classes/StrokeLog/index.d.ts +149 -0
- package/dist/lib/classes/StrokeLog/index.js +257 -0
- package/dist/lib/classes/Viewport/index.d.ts +108 -0
- package/dist/lib/classes/Viewport/index.js +194 -0
- package/dist/lib/document/index.d.ts +17 -0
- package/dist/lib/document/index.js +25 -0
- package/dist/lib/document/schema.d.ts +132 -0
- package/dist/lib/document/schema.js +110 -0
- package/dist/lib/i18n/index.d.ts +94 -0
- package/dist/lib/i18n/index.js +182 -0
- package/dist/lib/image/index.d.ts +90 -0
- package/dist/lib/image/index.js +147 -0
- package/dist/lib/messages/PatchSender/index.d.ts +75 -0
- package/dist/lib/messages/PatchSender/index.js +125 -0
- package/dist/lib/messages/index.d.ts +26 -0
- package/dist/lib/messages/index.js +59 -0
- package/dist/lib/messages/schema.d.ts +1775 -0
- package/dist/lib/messages/schema.js +192 -0
- package/dist/lib/presets/index.d.ts +32 -0
- package/dist/lib/presets/index.js +35 -0
- package/dist/lib/presets/schema.d.ts +753 -0
- package/dist/lib/presets/schema.js +181 -0
- package/dist/lib/rendering/BrushTip/index.d.ts +63 -0
- package/dist/lib/rendering/BrushTip/index.js +112 -0
- package/dist/lib/rendering/checkerboard/index.d.ts +22 -0
- package/dist/lib/rendering/checkerboard/index.js +31 -0
- package/dist/lib/rendering/compositing/index.d.ts +64 -0
- package/dist/lib/rendering/compositing/index.js +124 -0
- package/dist/lib/rendering/cursor/index.d.ts +14 -0
- package/dist/lib/rendering/cursor/index.js +39 -0
- package/dist/lib/rendering/cursors/index.d.ts +18 -0
- package/dist/lib/rendering/cursors/index.js +23 -0
- package/dist/lib/rendering/maskOutline/index.d.ts +22 -0
- package/dist/lib/rendering/maskOutline/index.js +78 -0
- package/dist/lib/rendering/outline/index.d.ts +36 -0
- package/dist/lib/rendering/outline/index.js +120 -0
- package/dist/lib/rendering/sample/index.d.ts +55 -0
- package/dist/lib/rendering/sample/index.js +83 -0
- package/dist/lib/rendering/screenshot/index.d.ts +67 -0
- package/dist/lib/rendering/screenshot/index.js +96 -0
- package/dist/lib/rendering/transformBox/index.d.ts +10 -0
- package/dist/lib/rendering/transformBox/index.js +57 -0
- package/dist/lib/selection/Lasso/index.d.ts +70 -0
- package/dist/lib/selection/Lasso/index.js +156 -0
- package/dist/lib/selection/Selection/index.d.ts +86 -0
- package/dist/lib/selection/Selection/index.js +228 -0
- package/dist/lib/selection/geometry/index.d.ts +37 -0
- package/dist/lib/selection/geometry/index.js +57 -0
- package/dist/lib/storage/index.d.ts +63 -0
- package/dist/lib/storage/index.js +114 -0
- package/dist/lib/transform/Transformer/index.d.ts +124 -0
- package/dist/lib/transform/Transformer/index.js +286 -0
- package/dist/lib/transform/geometry/index.d.ts +95 -0
- package/dist/lib/transform/geometry/index.js +258 -0
- package/dist/lib/utils/array/index.d.ts +10 -0
- package/dist/lib/utils/array/index.js +21 -0
- package/dist/lib/utils/gestures/index.d.ts +35 -0
- package/dist/lib/utils/gestures/index.js +81 -0
- package/dist/lib/utils/navigation/index.d.ts +51 -0
- package/dist/lib/utils/navigation/index.js +59 -0
- package/dist/lib/utils/random/index.d.ts +18 -0
- package/dist/lib/utils/random/index.js +27 -0
- package/dist/lib/utils/shortcuts/index.d.ts +74 -0
- package/dist/lib/utils/shortcuts/index.js +189 -0
- package/dist/lib/validation/validators.generated.d.ts +27 -0
- package/dist/lib/validation/validators.generated.js +19826 -0
- package/dist/locales/en-GB.js +188 -0
- package/dist/locales/en-US.js +188 -0
- package/dist/locales/fr.js +188 -0
- package/dist/locales/ja.js +188 -0
- package/dist/locales/ko.js +188 -0
- package/dist/locales/zh-Hans.js +188 -0
- package/dist/locales/zh-Hant.js +188 -0
- package/dist/package.js +3 -0
- package/dist/testing/app.d.ts +7 -0
- package/dist/testing/app.js +16 -0
- package/dist/testing/elements.d.ts +42 -0
- package/dist/testing/elements.js +58 -0
- package/dist/testing/fakeCanvas.d.ts +21 -0
- package/dist/testing/fakeCanvas.js +26 -0
- package/dist/testing/layerStore.d.ts +26 -0
- package/dist/testing/layerStore.js +43 -0
- package/dist/testing/locks.d.ts +15 -0
- package/dist/testing/locks.js +41 -0
- package/dist/testing/recordingCanvas.d.ts +14 -0
- package/dist/testing/recordingCanvas.js +28 -0
- package/dist/testing/recordingContext.d.ts +10 -0
- package/dist/testing/recordingContext.js +19 -0
- package/dist/ui/ChangeController/index.d.ts +40 -0
- package/dist/ui/ChangeController/index.js +62 -0
- package/dist/ui/base/index.d.ts +69 -0
- package/dist/ui/base/index.js +94 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +249 -0
- package/dist/ui/elements/PaintCanvas/index.js +584 -0
- package/dist/ui/elements/PaintCanvas/style.js +3 -0
- package/dist/ui/elements/PcBrushConfig/index.d.ts +130 -0
- package/dist/ui/elements/PcBrushConfig/index.js +463 -0
- package/dist/ui/elements/PcBrushConfig/style.js +3 -0
- package/dist/ui/elements/PcBrushMenu/index.d.ts +121 -0
- package/dist/ui/elements/PcBrushMenu/index.js +362 -0
- package/dist/ui/elements/PcBrushMenu/style.js +3 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +71 -0
- package/dist/ui/elements/PcBrushTest/index.js +136 -0
- package/dist/ui/elements/PcBrushTest/style.js +3 -0
- package/dist/ui/elements/PcButton/index.d.ts +57 -0
- package/dist/ui/elements/PcButton/index.js +124 -0
- package/dist/ui/elements/PcButtonGroup/index.d.ts +44 -0
- package/dist/ui/elements/PcButtonGroup/index.js +93 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +356 -0
- package/dist/ui/elements/PcCanvas/index.js +1004 -0
- package/dist/ui/elements/PcCanvas/style.js +3 -0
- package/dist/ui/elements/PcCanvasMap/index.d.ts +96 -0
- package/dist/ui/elements/PcCanvasMap/index.js +187 -0
- package/dist/ui/elements/PcCanvasMap/style.js +3 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +72 -0
- package/dist/ui/elements/PcContextMenu/index.js +148 -0
- package/dist/ui/elements/PcDialog/index.d.ts +63 -0
- package/dist/ui/elements/PcDialog/index.js +114 -0
- package/dist/ui/elements/PcIcon/index.d.ts +36 -0
- package/dist/ui/elements/PcIcon/index.js +43 -0
- package/dist/ui/elements/PcIconToggle/index.d.ts +46 -0
- package/dist/ui/elements/PcIconToggle/index.js +87 -0
- package/dist/ui/elements/PcInfoTip/index.d.ts +40 -0
- package/dist/ui/elements/PcInfoTip/index.js +95 -0
- package/dist/ui/elements/PcInputSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcInputSettings/index.js +150 -0
- package/dist/ui/elements/PcLayerItem/index.d.ts +221 -0
- package/dist/ui/elements/PcLayerItem/index.js +561 -0
- package/dist/ui/elements/PcLayerItem/style.js +3 -0
- package/dist/ui/elements/PcLayersMenu/index.d.ts +108 -0
- package/dist/ui/elements/PcLayersMenu/index.js +208 -0
- package/dist/ui/elements/PcLayersMenu/style.js +3 -0
- package/dist/ui/elements/PcMatch/index.d.ts +31 -0
- package/dist/ui/elements/PcMatch/index.js +58 -0
- package/dist/ui/elements/PcPanel/index.d.ts +43 -0
- package/dist/ui/elements/PcPanel/index.js +51 -0
- package/dist/ui/elements/PcPopover/index.d.ts +48 -0
- package/dist/ui/elements/PcPopover/index.js +86 -0
- package/dist/ui/elements/PcSlider/index.d.ts +57 -0
- package/dist/ui/elements/PcSlider/index.js +88 -0
- package/dist/ui/elements/PcStats/index.d.ts +54 -0
- package/dist/ui/elements/PcStats/index.js +141 -0
- package/dist/ui/elements/PcThemePicker/index.d.ts +56 -0
- package/dist/ui/elements/PcThemePicker/index.js +134 -0
- package/dist/ui/elements/PcToast/index.d.ts +58 -0
- package/dist/ui/elements/PcToast/index.js +110 -0
- package/dist/ui/elements/PcToggleCheckbox/index.d.ts +37 -0
- package/dist/ui/elements/PcToggleCheckbox/index.js +105 -0
- package/dist/ui/elements/PcToolSettings/index.d.ts +128 -0
- package/dist/ui/elements/PcToolSettings/index.js +543 -0
- package/dist/ui/elements/PcToolSettings/style.js +3 -0
- package/dist/ui/elements/PcToolbar/index.d.ts +71 -0
- package/dist/ui/elements/PcToolbar/index.js +190 -0
- package/dist/ui/elements/PcToolbar/style.js +3 -0
- package/dist/ui/icons/images/brush.js +3 -0
- package/dist/ui/icons/images/circle-mask.js +3 -0
- package/dist/ui/icons/images/clipboard.js +3 -0
- package/dist/ui/icons/images/cursor.js +3 -0
- package/dist/ui/icons/images/eraser.js +3 -0
- package/dist/ui/icons/images/eyedropper.js +3 -0
- package/dist/ui/icons/images/flip-horizontal.js +3 -0
- package/dist/ui/icons/images/flip-vertical.js +3 -0
- package/dist/ui/icons/images/focus-pen.js +3 -0
- package/dist/ui/icons/images/freehand-mask.js +3 -0
- package/dist/ui/icons/images/hidden.js +3 -0
- package/dist/ui/icons/images/layers.js +3 -0
- package/dist/ui/icons/images/linked.js +3 -0
- package/dist/ui/icons/images/locked.js +3 -0
- package/dist/ui/icons/images/moon.js +3 -0
- package/dist/ui/icons/images/pan.js +3 -0
- package/dist/ui/icons/images/polygon-mask.js +3 -0
- package/dist/ui/icons/images/pressure.js +3 -0
- package/dist/ui/icons/images/randomise.js +3 -0
- package/dist/ui/icons/images/rectangle-mask.js +3 -0
- package/dist/ui/icons/images/redo.js +3 -0
- package/dist/ui/icons/images/rotate-90-left.js +3 -0
- package/dist/ui/icons/images/rotate-90-right.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-quad.js +3 -0
- package/dist/ui/icons/images/smoothing-linear.js +3 -0
- package/dist/ui/icons/images/star.js +3 -0
- package/dist/ui/icons/images/sun.js +3 -0
- package/dist/ui/icons/images/tilt.js +3 -0
- package/dist/ui/icons/images/undo.js +3 -0
- package/dist/ui/icons/images/unlinked.js +3 -0
- package/dist/ui/icons/images/unlocked.js +3 -0
- package/dist/ui/icons/images/visible.js +3 -0
- package/dist/ui/icons/images/zoom-in.js +3 -0
- package/dist/ui/icons/images/zoom-out.js +3 -0
- package/dist/ui/icons/images/zoom.js +3 -0
- package/dist/ui/icons/index.d.ts +42 -0
- package/dist/ui/icons/index.js +130 -0
- package/dist/ui/index.d.ts +21 -0
- package/dist/ui/index.js +22 -0
- package/dist/ui/layout/index.d.ts +88 -0
- package/dist/ui/layout/index.js +96 -0
- package/dist/ui/styles/index.d.ts +13 -0
- package/dist/ui/styles/index.js +195 -0
- package/dist/ui/tokens/index.d.ts +29 -0
- package/dist/ui/tokens/index.js +114 -0
- package/locales/en-GB.json +186 -0
- package/locales/en-US.json +186 -0
- package/locales/fr.json +186 -0
- package/locales/ja.json +186 -0
- package/locales/ko.json +186 -0
- package/locales/zh-Hans.json +186 -0
- package/locales/zh-Hant.json +186 -0
- package/package.json +97 -3
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
import style_default from "./style.js";
|
|
2
|
+
import { PcElement, defineElement, emit } from "../../base/index.js";
|
|
3
|
+
import ChangeController from "../../ChangeController/index.js";
|
|
4
|
+
import PaintCanvas, { Panel } from "../../../lib/classes/PaintCanvas/index.js";
|
|
5
|
+
import KeyboardShortcuts, { SHORTCUT_EVENT } from "../../../lib/classes/KeyboardShortcuts/index.js";
|
|
6
|
+
import { isTouchDevice } from "../../../lib/classes/InputSettings/index.js";
|
|
7
|
+
import IndexedDbLayerStore from "../../../lib/storage/index.js";
|
|
8
|
+
import { AUTOSAVE_ERROR_EVENT, AutosaveState } from "../../../lib/classes/LayerAutosave/index.js";
|
|
9
|
+
import { isOwnAuthor } from "../../../lib/brushes/authors/index.js";
|
|
10
|
+
import { renderScreenshot, saveFile, screenshotName } from "../../../lib/rendering/screenshot/index.js";
|
|
11
|
+
import { DEFAULT_SIDE_MODES, STATE_EVENT, parseSideModes, sideModeFor } from "../../layout/index.js";
|
|
12
|
+
import { hasToolSettings } from "../PcToolSettings/index.js";
|
|
13
|
+
import { html, nothing, unsafeCSS } from "lit";
|
|
14
|
+
// src/ui/elements/PaintCanvas/index.ts
|
|
15
|
+
/** No space kept clear */
|
|
16
|
+
var NO_INSETS = {
|
|
17
|
+
top: 0,
|
|
18
|
+
right: 0,
|
|
19
|
+
bottom: 0,
|
|
20
|
+
left: 0
|
|
21
|
+
};
|
|
22
|
+
/** The choice that takes the drawing over from another window */
|
|
23
|
+
var TAKE_OVER_CHOICE = "take-over";
|
|
24
|
+
/** Elements that can take keyboard focus */
|
|
25
|
+
var FOCUSABLE = "button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])";
|
|
26
|
+
/**
|
|
27
|
+
* A ready-made painting app: the drawing elements arranged around the canvas, sized to its container (not the
|
|
28
|
+
* window), with slots to add or replace content:
|
|
29
|
+
*
|
|
30
|
+
* - `header`: across the top (empty by default)
|
|
31
|
+
* - `left`: `<pc-toolbar>` by default
|
|
32
|
+
* - `top`: `<pc-tool-settings>` (for tools that have settings) by default
|
|
33
|
+
* - `right`: empty by default (e.g. a `<pc-panel>` with a layers menu or chat)
|
|
34
|
+
* - `center`: `<pc-canvas>` by default
|
|
35
|
+
* - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
|
|
36
|
+
* - `bottom`: under the canvas (empty by default)
|
|
37
|
+
* - `footer`: across the bottom; `<pc-stats>` by default
|
|
38
|
+
* - `menu`: the app menu the toolbar's menu button opens (the button shows only with a menu)
|
|
39
|
+
*
|
|
40
|
+
* Content given for a slot replaces its default; an empty element clears it. Empty slots take no space. Hiding the
|
|
41
|
+
* interface (Tab key) leaves only the canvas and overlay. A `<pc-layers-menu>` given in a side (e.g. in a `<pc-panel>`)
|
|
42
|
+
* replaces the toolbar's layers menu button.
|
|
43
|
+
*
|
|
44
|
+
* Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
|
|
45
|
+
* rail@600"`): `docked` beside the canvas; `rail`, a column of buttons (one per `<pc-panel>`) that open each panel over
|
|
46
|
+
* the canvas; or `floating`, the side over the canvas as a palette that collapses to a button.
|
|
47
|
+
*
|
|
48
|
+
* Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
|
|
49
|
+
* `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
|
|
50
|
+
* handles keyboard shortcuts unless `shortcuts` is false.
|
|
51
|
+
*
|
|
52
|
+
* With a `store` name, the user's layers are kept in the browser (IndexedDB) under that name, so the drawing survives
|
|
53
|
+
* reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
|
|
54
|
+
* that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* html`<paint-canvas style="height: 600px"></paint-canvas>`
|
|
58
|
+
* html`<paint-canvas .app=${canvas} right-modes="docked, rail@800">
|
|
59
|
+
* <pc-panel slot="right" icon="layers" label="Layers"><pc-layers-menu .app=${canvas}></pc-layers-menu></pc-panel>
|
|
60
|
+
* </paint-canvas>`
|
|
61
|
+
*/
|
|
62
|
+
var PaintCanvasElement = class extends PcElement {
|
|
63
|
+
static properties = {
|
|
64
|
+
...PcElement.properties,
|
|
65
|
+
app: { attribute: false },
|
|
66
|
+
leftModes: {
|
|
67
|
+
type: String,
|
|
68
|
+
attribute: "left-modes"
|
|
69
|
+
},
|
|
70
|
+
rightModes: {
|
|
71
|
+
type: String,
|
|
72
|
+
attribute: "right-modes"
|
|
73
|
+
},
|
|
74
|
+
narrowWidth: {
|
|
75
|
+
type: Number,
|
|
76
|
+
attribute: "narrow-width"
|
|
77
|
+
},
|
|
78
|
+
menuText: {
|
|
79
|
+
type: String,
|
|
80
|
+
attribute: "menu-text"
|
|
81
|
+
},
|
|
82
|
+
menuLabel: {
|
|
83
|
+
type: String,
|
|
84
|
+
attribute: "menu-label"
|
|
85
|
+
},
|
|
86
|
+
shortcuts: { type: Boolean },
|
|
87
|
+
store: { type: String },
|
|
88
|
+
width: { state: true },
|
|
89
|
+
input: { state: true },
|
|
90
|
+
configuring: { state: true },
|
|
91
|
+
collapsed: { state: true },
|
|
92
|
+
openPanel: { state: true }
|
|
93
|
+
};
|
|
94
|
+
static styles = [PcElement.styles, unsafeCSS(style_default)];
|
|
95
|
+
/** Saves files (replaceable for tests) */
|
|
96
|
+
saveFile = saveFile;
|
|
97
|
+
changes = new ChangeController(this, () => [this.app, this.app?.autosave]);
|
|
98
|
+
/** The autosave the layout started for `store`, if any */
|
|
99
|
+
ownAutosave;
|
|
100
|
+
/** The autosave whose errors are shown */
|
|
101
|
+
watchedAutosave;
|
|
102
|
+
/** Whether drawing was blocked when shortcuts were last watched */
|
|
103
|
+
blockedShortcuts = false;
|
|
104
|
+
/** The panels in each side's slot */
|
|
105
|
+
panels = {
|
|
106
|
+
left: [],
|
|
107
|
+
right: []
|
|
108
|
+
};
|
|
109
|
+
/** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
|
|
110
|
+
sideLayers = {
|
|
111
|
+
left: false,
|
|
112
|
+
right: false
|
|
113
|
+
};
|
|
114
|
+
/** Whether the implementer gave a menu */
|
|
115
|
+
hasMenu = false;
|
|
116
|
+
/** Slots the implementer gave content for (their defaults aren't created) */
|
|
117
|
+
filled = {};
|
|
118
|
+
/** Keyboard shortcuts, while handled */
|
|
119
|
+
keys;
|
|
120
|
+
/** The state last reported, to report only changes */
|
|
121
|
+
reported;
|
|
122
|
+
/** Space the floating sides cover over the canvas */
|
|
123
|
+
floatingInsets = NO_INSETS;
|
|
124
|
+
/** The rail button that opened the open panel, to return focus to */
|
|
125
|
+
opener;
|
|
126
|
+
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(([entry]) => {
|
|
127
|
+
this.width = Math.round(entry?.contentRect.width ?? 0);
|
|
128
|
+
}) : void 0;
|
|
129
|
+
panelObserver = new MutationObserver(() => this.requestUpdate());
|
|
130
|
+
onPointerDown = (event) => {
|
|
131
|
+
const kind = event.pointerType;
|
|
132
|
+
this.input = kind === "pen" || kind === "touch" ? kind : "mouse";
|
|
133
|
+
};
|
|
134
|
+
onAutosaveError = () => emit(this, "pc-toast", {
|
|
135
|
+
message: this.t("autosave.failed"),
|
|
136
|
+
kind: "error"
|
|
137
|
+
});
|
|
138
|
+
onShortcut = (event) => {
|
|
139
|
+
if (event.detail.type === "save") this.saveScreenshot();
|
|
140
|
+
};
|
|
141
|
+
onDocumentPointerDown = (event) => {
|
|
142
|
+
const panel = this.openPanel;
|
|
143
|
+
if (!panel) return;
|
|
144
|
+
const path = event.composedPath();
|
|
145
|
+
if (!path.includes(panel) && !(this.opener && path.includes(this.opener))) this.closePanel(false);
|
|
146
|
+
};
|
|
147
|
+
onKeyDown = (event) => {
|
|
148
|
+
if (event.key !== "Escape" || !this.openPanel) return;
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
this.closePanel(true);
|
|
151
|
+
};
|
|
152
|
+
constructor() {
|
|
153
|
+
super();
|
|
154
|
+
this.leftModes = DEFAULT_SIDE_MODES;
|
|
155
|
+
this.rightModes = DEFAULT_SIDE_MODES;
|
|
156
|
+
this.narrowWidth = 600;
|
|
157
|
+
this.menuText = "☰";
|
|
158
|
+
this.shortcuts = true;
|
|
159
|
+
this.width = 0;
|
|
160
|
+
this.input = isTouchDevice() ? "touch" : "mouse";
|
|
161
|
+
this.collapsed = {
|
|
162
|
+
left: true,
|
|
163
|
+
right: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** Whether another window is drawing on the stored drawing, so this one is blocked */
|
|
167
|
+
get blocked() {
|
|
168
|
+
return this.app?.autosave?.state === AutosaveState.Elsewhere;
|
|
169
|
+
}
|
|
170
|
+
/** The layout's state now (see `LayoutState`) */
|
|
171
|
+
get state() {
|
|
172
|
+
return {
|
|
173
|
+
width: this.width,
|
|
174
|
+
narrow: this.width < this.narrowWidth,
|
|
175
|
+
input: this.input,
|
|
176
|
+
uiHidden: this.app?.interfaceVisible === false,
|
|
177
|
+
leftMode: this.modeOf("left"),
|
|
178
|
+
rightMode: this.modeOf("right")
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** A side's mode for the layout's width */
|
|
182
|
+
modeOf(side) {
|
|
183
|
+
return sideModeFor(parseSideModes(side === "left" ? this.leftModes : this.rightModes), this.width);
|
|
184
|
+
}
|
|
185
|
+
connectedCallback() {
|
|
186
|
+
this.app ??= new PaintCanvas();
|
|
187
|
+
super.connectedCallback();
|
|
188
|
+
this.resizeObserver?.observe(this);
|
|
189
|
+
this.panelObserver.observe(this, {
|
|
190
|
+
subtree: true,
|
|
191
|
+
attributes: true,
|
|
192
|
+
attributeFilter: [
|
|
193
|
+
"icon",
|
|
194
|
+
"label",
|
|
195
|
+
"badge"
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
this.addEventListener("pointerdown", this.onPointerDown, true);
|
|
199
|
+
this.addEventListener("keydown", this.onKeyDown);
|
|
200
|
+
document.addEventListener("pointerdown", this.onDocumentPointerDown, true);
|
|
201
|
+
this.watchShortcuts();
|
|
202
|
+
}
|
|
203
|
+
disconnectedCallback() {
|
|
204
|
+
super.disconnectedCallback();
|
|
205
|
+
this.resizeObserver?.disconnect();
|
|
206
|
+
this.panelObserver.disconnect();
|
|
207
|
+
this.removeEventListener("pointerdown", this.onPointerDown, true);
|
|
208
|
+
this.removeEventListener("keydown", this.onKeyDown);
|
|
209
|
+
document.removeEventListener("pointerdown", this.onDocumentPointerDown, true);
|
|
210
|
+
this.unwatchShortcuts();
|
|
211
|
+
}
|
|
212
|
+
willUpdate(changed) {
|
|
213
|
+
super.willUpdate(changed);
|
|
214
|
+
if (changed.has("app") || changed.has("store")) this.useStore(changed.get("app"));
|
|
215
|
+
this.watchAutosaveErrors();
|
|
216
|
+
if (this.isConnected && (changed.has("app") || changed.has("shortcuts") || this.blocked !== this.blockedShortcuts)) this.watchShortcuts();
|
|
217
|
+
const panel = this.openPanel;
|
|
218
|
+
if (panel && this.modeOf(this.sideOf(panel)) !== "rail") this.closePanel(false);
|
|
219
|
+
}
|
|
220
|
+
updated() {
|
|
221
|
+
const state = this.state;
|
|
222
|
+
this.toggleAttribute("narrow", state.narrow);
|
|
223
|
+
this.toggleAttribute("ui-hidden", state.uiHidden);
|
|
224
|
+
this.setAttribute("input", state.input);
|
|
225
|
+
this.setAttribute("left-mode", state.leftMode);
|
|
226
|
+
this.setAttribute("right-mode", state.rightMode);
|
|
227
|
+
const report = JSON.stringify(state);
|
|
228
|
+
if (report !== this.reported) {
|
|
229
|
+
this.reported = report;
|
|
230
|
+
this.dispatchEvent(new CustomEvent(STATE_EVENT, { detail: state }));
|
|
231
|
+
}
|
|
232
|
+
this.measureFloatingSides();
|
|
233
|
+
}
|
|
234
|
+
/** Keep the canvas clear of floating sides when fitting it */
|
|
235
|
+
measureFloatingSides() {
|
|
236
|
+
const width = (side) => this.renderRoot.querySelector(`.side.${side} .float:not(.collapsed)`)?.getBoundingClientRect().width ?? 0;
|
|
237
|
+
const insets = {
|
|
238
|
+
...NO_INSETS,
|
|
239
|
+
left: width("left"),
|
|
240
|
+
right: width("right")
|
|
241
|
+
};
|
|
242
|
+
if (insets.left === this.floatingInsets.left && insets.right === this.floatingInsets.right) return;
|
|
243
|
+
this.floatingInsets = insets;
|
|
244
|
+
this.requestUpdate();
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Keep the canvas's layers under the `store` name, replacing an autosave the layout started earlier (also on the
|
|
248
|
+
* previous canvas). An autosave the implementer started is left alone when there's no name.
|
|
249
|
+
*/
|
|
250
|
+
useStore(previousApp) {
|
|
251
|
+
const app = this.app;
|
|
252
|
+
const own = this.ownAutosave;
|
|
253
|
+
if (own && previousApp?.autosave === own) previousApp.useLayerStore(void 0);
|
|
254
|
+
const name = this.store || void 0;
|
|
255
|
+
if (name === void 0) {
|
|
256
|
+
if (own && app.autosave === own) this.ownAutosave = app.useLayerStore(void 0);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (app.autosave?.store.name !== name) this.ownAutosave = app.useLayerStore(new IndexedDbLayerStore(name));
|
|
260
|
+
}
|
|
261
|
+
/** Show errors from the canvas's current autosave */
|
|
262
|
+
watchAutosaveErrors() {
|
|
263
|
+
const autosave = this.app?.autosave;
|
|
264
|
+
if (autosave === this.watchedAutosave) return;
|
|
265
|
+
this.watchedAutosave?.removeEventListener(AUTOSAVE_ERROR_EVENT, this.onAutosaveError);
|
|
266
|
+
this.watchedAutosave = autosave;
|
|
267
|
+
autosave?.addEventListener(AUTOSAVE_ERROR_EVENT, this.onAutosaveError);
|
|
268
|
+
}
|
|
269
|
+
/** Handle keyboard shortcuts for the current canvas, if asked to (not while blocked) */
|
|
270
|
+
watchShortcuts() {
|
|
271
|
+
this.unwatchShortcuts();
|
|
272
|
+
this.blockedShortcuts = this.blocked;
|
|
273
|
+
if (!this.shortcuts || !this.app || this.blockedShortcuts) return;
|
|
274
|
+
this.keys = new KeyboardShortcuts(this.app);
|
|
275
|
+
this.keys.attach(window);
|
|
276
|
+
window.addEventListener(SHORTCUT_EVENT, this.onShortcut);
|
|
277
|
+
}
|
|
278
|
+
/** Stop handling keyboard shortcuts */
|
|
279
|
+
unwatchShortcuts() {
|
|
280
|
+
this.keys?.detach();
|
|
281
|
+
this.keys = void 0;
|
|
282
|
+
window.removeEventListener(SHORTCUT_EVENT, this.onShortcut);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Save the whole canvas as a PNG on white, at full size, including a stroke or transform in progress. Returns how it
|
|
286
|
+
* was saved, or undefined if cancelled.
|
|
287
|
+
*/
|
|
288
|
+
async saveScreenshot() {
|
|
289
|
+
const app = this.app;
|
|
290
|
+
const blob = await renderScreenshot(app.drawables, app.width, app.height, app.transform.preview ?? app.strokes.preview);
|
|
291
|
+
return this.saveFile(blob, screenshotName(void 0, app.names.filePrefix));
|
|
292
|
+
}
|
|
293
|
+
/** The brush or eraser settings */
|
|
294
|
+
toolSettings(kind) {
|
|
295
|
+
const app = this.app;
|
|
296
|
+
return kind === "eraser" ? app.eraser : app.brush;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Open the configuration window for the current brush or eraser settings. When the selected preset is one of the
|
|
300
|
+
* user's own, the window can delete it.
|
|
301
|
+
*/
|
|
302
|
+
configure(kind) {
|
|
303
|
+
const app = this.app;
|
|
304
|
+
const settings = this.toolSettings(kind);
|
|
305
|
+
const selected = settings.selectedPreset;
|
|
306
|
+
const own = selected && !app.isBuiltInPreset(selected.id) && isOwnAuthor(selected.author, app.authorHandle, app.names.unknownAuthor);
|
|
307
|
+
this.configuring = {
|
|
308
|
+
kind,
|
|
309
|
+
config: {
|
|
310
|
+
...settings.preset,
|
|
311
|
+
width: settings.width,
|
|
312
|
+
height: settings.height
|
|
313
|
+
},
|
|
314
|
+
...own ? { editingId: selected.id } : {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Open the configuration window to make a new preset, starting from the current brush or eraser settings.
|
|
319
|
+
*/
|
|
320
|
+
newPreset(kind) {
|
|
321
|
+
const settings = this.toolSettings(kind);
|
|
322
|
+
this.app?.closePanel();
|
|
323
|
+
this.configuring = {
|
|
324
|
+
kind,
|
|
325
|
+
config: {
|
|
326
|
+
...settings.preset,
|
|
327
|
+
name: this.t("brushConfig.newPresetName"),
|
|
328
|
+
width: settings.width,
|
|
329
|
+
height: settings.height
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/** Which side a panel is in */
|
|
334
|
+
sideOf(panel) {
|
|
335
|
+
return panel.slot === "left" ? "left" : "right";
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Open a panel from its rail button (closing any other), or close it if it's open. Focus moves into the panel.
|
|
339
|
+
*/
|
|
340
|
+
togglePanel(panel, opener) {
|
|
341
|
+
if (this.openPanel === panel) {
|
|
342
|
+
this.closePanel(true);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (this.openPanel) this.closePanel(false);
|
|
346
|
+
this.openPanel = panel;
|
|
347
|
+
this.opener = opener;
|
|
348
|
+
panel.toggleAttribute("data-pc-open", true);
|
|
349
|
+
const focusable = panel.querySelector(FOCUSABLE);
|
|
350
|
+
if (focusable) focusable.focus();
|
|
351
|
+
else {
|
|
352
|
+
panel.tabIndex = -1;
|
|
353
|
+
panel.focus();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Close the panel open from a rail, moving focus back to its rail button if asked.
|
|
358
|
+
*/
|
|
359
|
+
closePanel(returnFocus) {
|
|
360
|
+
const panel = this.openPanel;
|
|
361
|
+
if (!panel) return;
|
|
362
|
+
panel.removeAttribute("data-pc-open");
|
|
363
|
+
this.openPanel = void 0;
|
|
364
|
+
if (returnFocus) this.opener?.focus();
|
|
365
|
+
this.opener = void 0;
|
|
366
|
+
}
|
|
367
|
+
/** Follow which slots have content given (their defaults aren't needed) */
|
|
368
|
+
onSlotChange(event) {
|
|
369
|
+
const slot = event.target;
|
|
370
|
+
this.filled = {
|
|
371
|
+
...this.filled,
|
|
372
|
+
[slot.name]: slot.assignedElements().length > 0
|
|
373
|
+
};
|
|
374
|
+
this.requestUpdate();
|
|
375
|
+
}
|
|
376
|
+
/** A slot, with its default content unless content was given for it */
|
|
377
|
+
slotWithDefault(name, fallback) {
|
|
378
|
+
return html`<slot name=${name} @slotchange=${this.onSlotChange}
|
|
379
|
+
>${this.filled[name] ? nothing : fallback}</slot
|
|
380
|
+
>`;
|
|
381
|
+
}
|
|
382
|
+
/** Follow the panels given for a side, and whether it has a layers menu */
|
|
383
|
+
onSideSlotChange(side, event) {
|
|
384
|
+
const assigned = event.target.assignedElements();
|
|
385
|
+
this.panels = {
|
|
386
|
+
...this.panels,
|
|
387
|
+
[side]: assigned.filter((element) => element.localName === "pc-panel")
|
|
388
|
+
};
|
|
389
|
+
this.sideLayers = {
|
|
390
|
+
...this.sideLayers,
|
|
391
|
+
[side]: assigned.some((element) => element.matches("pc-layers-menu, :has(pc-layers-menu)"))
|
|
392
|
+
};
|
|
393
|
+
this.onSlotChange(event);
|
|
394
|
+
}
|
|
395
|
+
/** Follow whether a menu was given */
|
|
396
|
+
onMenuSlotChange(event) {
|
|
397
|
+
this.hasMenu = event.target.assignedElements().length > 0;
|
|
398
|
+
this.requestUpdate();
|
|
399
|
+
}
|
|
400
|
+
/** Whether a side has a layers menu, replacing the toolbar's */
|
|
401
|
+
get layersInSide() {
|
|
402
|
+
return this.sideLayers.left || this.sideLayers.right;
|
|
403
|
+
}
|
|
404
|
+
/** The toolbar shown on the left unless the implementer gives other content */
|
|
405
|
+
renderToolbar(app) {
|
|
406
|
+
return html`<pc-toolbar
|
|
407
|
+
.app=${app}
|
|
408
|
+
.menu=${this.hasMenu}
|
|
409
|
+
.layers=${!this.layersInSide}
|
|
410
|
+
.menuText=${this.menuText}
|
|
411
|
+
.menuLabel=${this.menuLabel}
|
|
412
|
+
></pc-toolbar>`;
|
|
413
|
+
}
|
|
414
|
+
/** A side's rail buttons, one per panel */
|
|
415
|
+
renderRail(side, label) {
|
|
416
|
+
const panels = this.panels[side];
|
|
417
|
+
if (panels.length === 0) return nothing;
|
|
418
|
+
return html`<div
|
|
419
|
+
class="rail"
|
|
420
|
+
part="${side}-rail"
|
|
421
|
+
role="toolbar"
|
|
422
|
+
aria-orientation="vertical"
|
|
423
|
+
aria-label=${label}
|
|
424
|
+
>
|
|
425
|
+
${panels.map((panel) => html`<button
|
|
426
|
+
type="button"
|
|
427
|
+
title=${panel.label}
|
|
428
|
+
aria-label=${panel.badge ? this.t("layout.new", { name: panel.label }) : panel.label}
|
|
429
|
+
aria-expanded=${String(this.openPanel === panel)}
|
|
430
|
+
@click=${(event) => this.togglePanel(panel, event.currentTarget)}
|
|
431
|
+
>
|
|
432
|
+
<img src=${this.iconUrl(panel.icon)} alt="" />
|
|
433
|
+
${panel.badge ? html`<span class="badge"></span>` : nothing}
|
|
434
|
+
</button>`)}
|
|
435
|
+
</div>`;
|
|
436
|
+
}
|
|
437
|
+
/** A side, in its mode */
|
|
438
|
+
renderSide(side, app, blocked) {
|
|
439
|
+
const mode = this.modeOf(side);
|
|
440
|
+
const label = this.t(side === "left" ? "layout.tools" : "layout.panels");
|
|
441
|
+
const slot = html`<slot
|
|
442
|
+
name=${side}
|
|
443
|
+
@slotchange=${(event) => this.onSideSlotChange(side, event)}
|
|
444
|
+
>${side === "left" && !this.filled.left ? this.renderToolbar(app) : nothing}</slot
|
|
445
|
+
>`;
|
|
446
|
+
let content = slot;
|
|
447
|
+
if (mode === "rail") content = side === "left" ? html`${slot}${this.renderRail(side, label)}` : html`${this.renderRail(side, label)}${slot}`;
|
|
448
|
+
else if (mode === "floating") {
|
|
449
|
+
const collapsed = this.collapsed[side];
|
|
450
|
+
const arrows = side === "left" ? ["»", "«"] : ["«", "»"];
|
|
451
|
+
content = html`<div
|
|
452
|
+
class=${collapsed ? "float collapsed" : "float"}
|
|
453
|
+
part="${side}-float"
|
|
454
|
+
>
|
|
455
|
+
<button
|
|
456
|
+
type="button"
|
|
457
|
+
class="collapse"
|
|
458
|
+
aria-expanded=${String(!collapsed)}
|
|
459
|
+
aria-label=${this.t(collapsed ? "layout.show" : "layout.hide", { name: label })}
|
|
460
|
+
@click=${() => this.collapsed = {
|
|
461
|
+
...this.collapsed,
|
|
462
|
+
[side]: !collapsed
|
|
463
|
+
}}
|
|
464
|
+
>
|
|
465
|
+
${collapsed ? arrows[0] : arrows[1]}
|
|
466
|
+
</button>
|
|
467
|
+
<div class="content" ?hidden=${collapsed}>${slot}</div>
|
|
468
|
+
</div>`;
|
|
469
|
+
}
|
|
470
|
+
return html`<div
|
|
471
|
+
class="side ${side}"
|
|
472
|
+
part=${side}
|
|
473
|
+
data-mode=${mode}
|
|
474
|
+
role="region"
|
|
475
|
+
aria-label=${label}
|
|
476
|
+
?inert=${blocked}
|
|
477
|
+
>
|
|
478
|
+
${content}
|
|
479
|
+
</div>`;
|
|
480
|
+
}
|
|
481
|
+
/** The menus the toolbar opens, floating over the canvas beside it */
|
|
482
|
+
renderPanels(app) {
|
|
483
|
+
const brushMenu = (kind) => html`<pc-brush-menu
|
|
484
|
+
class="panel top-panel"
|
|
485
|
+
kind=${kind}
|
|
486
|
+
.app=${app}
|
|
487
|
+
@pc-new-preset=${() => this.newPreset(kind)}
|
|
488
|
+
></pc-brush-menu>`;
|
|
489
|
+
return html`${app.panel === Panel.Brush ? brushMenu("brush") : nothing}
|
|
490
|
+
${app.panel === Panel.Eraser ? brushMenu("eraser") : nothing}
|
|
491
|
+
${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-layers-menu class="panel" .app=${app}></pc-layers-menu>` : nothing}
|
|
492
|
+
<div class="panel menu-panel" ?open=${app.panel === Panel.Menu}>
|
|
493
|
+
<slot name="menu" @slotchange=${this.onMenuSlotChange}></slot>
|
|
494
|
+
</div>`;
|
|
495
|
+
}
|
|
496
|
+
/** The brush configuration window, while open */
|
|
497
|
+
renderConfig(app) {
|
|
498
|
+
const configuring = this.configuring;
|
|
499
|
+
if (!configuring) return nothing;
|
|
500
|
+
return html`<pc-brush-config
|
|
501
|
+
.app=${app}
|
|
502
|
+
kind=${configuring.kind}
|
|
503
|
+
.config=${configuring.config}
|
|
504
|
+
editingId=${configuring.editingId ?? nothing}
|
|
505
|
+
@pc-save=${(event) => {
|
|
506
|
+
this.toolSettings(configuring.kind).apply(event.detail);
|
|
507
|
+
this.configuring = void 0;
|
|
508
|
+
}}
|
|
509
|
+
@pc-close=${() => this.configuring = void 0}
|
|
510
|
+
></pc-brush-config>`;
|
|
511
|
+
}
|
|
512
|
+
/** The message blocking the layout while another window is drawing on the stored drawing */
|
|
513
|
+
renderBlocked(app) {
|
|
514
|
+
const choices = [{
|
|
515
|
+
value: TAKE_OVER_CHOICE,
|
|
516
|
+
label: this.t("autosave.takeOver")
|
|
517
|
+
}];
|
|
518
|
+
return html`<pc-dialog
|
|
519
|
+
class="elsewhere"
|
|
520
|
+
heading=${this.t("autosave.elsewhere")}
|
|
521
|
+
message=${this.t("autosave.elsewhereMessage")}
|
|
522
|
+
.choices=${choices}
|
|
523
|
+
@pc-choose=${(event) => {
|
|
524
|
+
if (event.detail.value === TAKE_OVER_CHOICE) app.autosave?.takeOver();
|
|
525
|
+
}}
|
|
526
|
+
></pc-dialog>`;
|
|
527
|
+
}
|
|
528
|
+
render() {
|
|
529
|
+
const app = this.app;
|
|
530
|
+
const showsToolSettings = app.interfaceVisible && hasToolSettings(app.tool);
|
|
531
|
+
const blocked = this.blocked;
|
|
532
|
+
return html`<header part="header" ?inert=${blocked}>
|
|
533
|
+
<slot name="header"></slot>
|
|
534
|
+
</header>
|
|
535
|
+
${this.renderSide("left", app, blocked)}
|
|
536
|
+
<div
|
|
537
|
+
class="top"
|
|
538
|
+
part="top"
|
|
539
|
+
role="region"
|
|
540
|
+
aria-label=${this.t("layout.toolSettings")}
|
|
541
|
+
?inert=${blocked}
|
|
542
|
+
>
|
|
543
|
+
${this.slotWithDefault("top", showsToolSettings ? html`<pc-tool-settings
|
|
544
|
+
.app=${app}
|
|
545
|
+
@pc-configure-brush=${(event) => this.configure(event.detail)}
|
|
546
|
+
></pc-tool-settings>` : nothing)}
|
|
547
|
+
</div>
|
|
548
|
+
<div
|
|
549
|
+
class="center"
|
|
550
|
+
part="center"
|
|
551
|
+
role="region"
|
|
552
|
+
aria-label=${this.t("layout.canvas")}
|
|
553
|
+
?inert=${blocked}
|
|
554
|
+
>
|
|
555
|
+
${this.slotWithDefault("center", html`<pc-canvas
|
|
556
|
+
.app=${app}
|
|
557
|
+
.insets=${this.floatingInsets}
|
|
558
|
+
></pc-canvas>`)}
|
|
559
|
+
<div class="overlay" part="overlay">
|
|
560
|
+
${this.slotWithDefault("overlay", html`<pc-canvas-map
|
|
561
|
+
.app=${app}
|
|
562
|
+
.insets=${this.floatingInsets}
|
|
563
|
+
></pc-canvas-map>`)}
|
|
564
|
+
</div>
|
|
565
|
+
${app.interfaceVisible ? this.renderPanels(app) : nothing}
|
|
566
|
+
</div>
|
|
567
|
+
<div
|
|
568
|
+
class="bottom"
|
|
569
|
+
part="bottom"
|
|
570
|
+
role="region"
|
|
571
|
+
aria-label=${this.t("layout.status")}
|
|
572
|
+
?inert=${blocked}
|
|
573
|
+
>
|
|
574
|
+
<slot name="bottom"></slot>
|
|
575
|
+
</div>
|
|
576
|
+
${this.renderSide("right", app, blocked)}
|
|
577
|
+
<footer part="footer" ?inert=${blocked}>
|
|
578
|
+
${this.slotWithDefault("footer", html`<pc-stats .app=${app}></pc-stats>`)}
|
|
579
|
+
</footer>
|
|
580
|
+
${this.renderConfig(app)} ${blocked ? this.renderBlocked(app) : nothing}`;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
defineElement("paint-canvas", PaintCanvasElement);
|
|
584
|
+
export { PaintCanvasElement };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/ui/elements/PaintCanvas/style.css?inline
|
|
2
|
+
var style_default = "/* The layout: header across the top, footer across the bottom, sides left and right, and between them the tool\n settings, the canvas and the bottom bar. Empty areas take no space. */\n:host {\n position: relative;\n display: grid;\n grid-template:\n \"header header header\" auto\n \"left top right\" auto\n \"left center right\" 1fr\n \"left bottom right\" auto\n \"footer footer footer\" auto / auto 1fr auto;\n overflow: hidden;\n background: var(--pc-bar);\n font-family: var(--pc-font);\n font-size: var(--pc-font-size);\n}\nheader {\n grid-area: header;\n}\n.top {\n grid-area: top;\n min-width: 0;\n}\n.center {\n position: relative;\n grid-area: center;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.bottom {\n grid-area: bottom;\n min-width: 0;\n}\nfooter {\n grid-area: footer;\n}\n.side {\n position: relative;\n z-index: 3;\n display: flex;\n min-height: 0;\n}\n.side.left {\n grid-area: left;\n}\n.side.right {\n grid-area: right;\n}\n\n/* Hiding the interface (Tab key) leaves only the canvas */\n:host([ui-hidden]) header,\n:host([ui-hidden]) .top,\n:host([ui-hidden]) .bottom,\n:host([ui-hidden]) footer,\n:host([ui-hidden]) .side {\n display: none;\n}\n\n/* The canvas fills the middle; things drawn over it go in the overlay */\n.center ::slotted([slot=\"center\"]),\n.center pc-canvas {\n position: absolute;\n inset: 0;\n}\n.overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.overlay > * {\n pointer-events: auto;\n}\npc-canvas-map {\n position: absolute;\n right: 20px;\n bottom: 20px;\n}\n\n/* Menus opened from the toolbar float over the canvas, beside the toolbar */\n.panel {\n position: absolute;\n z-index: 4;\n left: 0;\n bottom: 0;\n max-height: 100%;\n overflow: auto;\n}\n.panel.top-panel {\n top: 0;\n bottom: auto;\n}\n.menu-panel:not([open]) {\n display: none;\n}\n\n/* Rail: a column of buttons, one per panel; the open panel floats over the canvas beside it */\n.rail {\n display: flex;\n flex-direction: column;\n gap: var(--pc-space-1);\n padding: var(--pc-space-1);\n border-inline: 1px solid var(--pc-border);\n background: var(--pc-bar);\n}\n.rail button {\n position: relative;\n display: inline-flex;\n padding: var(--pc-space-1);\n border: 1px solid transparent;\n border-radius: var(--pc-radius-small);\n background: transparent;\n cursor: pointer;\n}\n.rail button:hover {\n background: var(--pc-surface-hover);\n}\n.rail button[aria-expanded=\"true\"] {\n border-color: var(--pc-accent);\n background: var(--pc-accent);\n}\n.rail button:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.rail img {\n width: var(--pc-icon-size);\n height: var(--pc-icon-size);\n filter: var(--pc-icon-filter);\n}\n.rail .badge {\n position: absolute;\n top: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border: 2px solid var(--pc-bar);\n border-radius: 50%;\n background: var(--pc-red);\n}\n.side[data-mode=\"rail\"] ::slotted(pc-panel) {\n display: none;\n}\n.side[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n position: absolute;\n z-index: 5;\n top: 0;\n bottom: 0;\n display: flex;\n width: var(--pc-floating-panel-width, min(360px, 85vw));\n background: var(--pc-surface-raised);\n box-shadow: var(--pc-shadow);\n}\n/* When narrow, a layers menu fills the panel opened from the rail (a floating side sizes itself to its content) */\n:host([narrow]) .side[data-mode=\"rail\"] ::slotted(pc-panel) {\n --pc-layers-menu-width: 100%;\n}\n.side.left[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n left: 100%;\n}\n.side.right[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n right: 100%;\n}\n\n/* Floating: the side floats over the canvas as a palette, and collapses to a button */\n.side[data-mode=\"floating\"] {\n /* Beside the canvas only, so it floats over the canvas and not the tool settings */\n grid-row: 3;\n width: 0;\n}\n.float {\n position: absolute;\n z-index: 5;\n top: var(--pc-space-2);\n bottom: var(--pc-space-2);\n display: flex;\n flex-direction: column;\n gap: var(--pc-space-1);\n max-width: var(--pc-floating-panel-width, min(360px, 85vw));\n border-radius: var(--pc-radius);\n background: var(--pc-surface-raised);\n box-shadow: var(--pc-shadow);\n overflow: hidden;\n}\n.side.left .float {\n left: var(--pc-space-2);\n}\n.side.right .float {\n right: var(--pc-space-2);\n}\n.float.collapsed {\n bottom: auto;\n}\n.float .collapse {\n align-self: flex-end;\n min-width: var(--pc-control-size);\n min-height: var(--pc-control-size);\n border: 0;\n background: transparent;\n color: var(--pc-text);\n font-size: 18px;\n cursor: pointer;\n}\n.float .collapse:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.float .content {\n display: flex;\n min-height: 0;\n flex: 1;\n}\n.float .content[hidden] {\n display: none;\n}\n\n/* While another window is drawing, its message covers only the layout (not the whole page) */\npc-dialog.elsewhere {\n position: absolute;\n}\n";
|
|
3
|
+
export { style_default as default };
|