@lvl99dotcom/paintcanvas 0.3.0 → 0.4.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/README.md +1 -1
- package/dist/lib/brushes/StrokeEngine/index.d.ts +2 -2
- package/dist/lib/brushes/StrokeEngine/index.js +26 -28
- package/dist/lib/classes/BrushSettings/index.d.ts +24 -0
- package/dist/lib/classes/BrushSettings/index.js +35 -1
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +17 -6
- package/dist/lib/classes/KeyboardShortcuts/index.js +81 -18
- package/dist/lib/classes/PaintCanvas/index.d.ts +77 -27
- package/dist/lib/classes/PaintCanvas/index.js +143 -42
- package/dist/lib/classes/PaintCanvas/tool.d.ts +15 -0
- package/dist/lib/classes/PaintCanvas/tool.js +18 -0
- package/dist/lib/classes/Palette/index.d.ts +58 -0
- package/dist/lib/classes/Palette/index.js +92 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +52 -0
- package/dist/lib/classes/SettingsAutosave/index.js +96 -0
- package/dist/lib/classes/ShortcutSettings/index.d.ts +63 -0
- package/dist/lib/classes/ShortcutSettings/index.js +110 -0
- package/dist/lib/palette/index.d.ts +49 -0
- package/dist/lib/palette/index.js +82 -0
- package/dist/lib/palette/schema.d.ts +45 -0
- package/dist/lib/palette/schema.js +41 -0
- package/dist/lib/rendering/cursors/index.d.ts +2 -0
- package/dist/lib/rendering/cursors/index.js +3 -1
- package/dist/lib/rendering/sample/index.js +3 -2
- package/dist/lib/settings/index.d.ts +63 -0
- package/dist/lib/settings/index.js +91 -0
- package/dist/lib/settings/schema.d.ts +150 -0
- package/dist/lib/settings/schema.js +102 -0
- package/dist/lib/storage/index.d.ts +36 -5
- package/dist/lib/storage/index.js +87 -29
- package/dist/lib/utils/colour/index.d.ts +72 -0
- package/dist/lib/utils/colour/index.js +154 -0
- package/dist/lib/utils/shortcuts/index.d.ts +174 -11
- package/dist/lib/utils/shortcuts/index.js +391 -145
- package/dist/lib/validation/validators.generated.d.ts +5 -0
- package/dist/lib/validation/validators.generated.js +872 -1
- package/dist/locales/en-GB.js +87 -1
- package/dist/locales/en-US.js +87 -1
- package/dist/locales/fr.js +87 -1
- package/dist/locales/ja.js +87 -1
- package/dist/locales/ko.js +87 -1
- package/dist/locales/zh-Hans.js +87 -1
- package/dist/locales/zh-Hant.js +87 -1
- package/dist/package.js +1 -1
- package/dist/ui/elements/PaintCanvas/index.d.ts +49 -47
- package/dist/ui/elements/PaintCanvas/index.js +128 -169
- package/dist/ui/elements/PaintCanvas/style.js +1 -1
- package/dist/ui/elements/PcAppSettings/index.d.ts +59 -0
- package/dist/ui/elements/PcAppSettings/index.js +222 -0
- package/dist/ui/elements/PcBrushConfig/index.js +1 -0
- package/dist/ui/elements/PcButton/index.js +1 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +1 -1
- package/dist/ui/elements/PcCanvas/index.js +7 -5
- package/dist/ui/elements/PcColourPair/index.d.ts +43 -0
- package/dist/ui/elements/PcColourPair/index.js +125 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +70 -33
- package/dist/ui/elements/PcContextMenu/index.js +128 -97
- package/dist/ui/elements/PcDialog/index.js +1 -0
- package/dist/ui/elements/PcIconToggle/index.js +1 -0
- package/dist/ui/elements/PcLayerItem/index.d.ts +13 -34
- package/dist/ui/elements/PcLayerItem/index.js +39 -91
- package/dist/ui/elements/PcLayerItem/style.js +1 -1
- package/dist/ui/elements/PcMenu/index.d.ts +93 -0
- package/dist/ui/elements/PcMenu/index.js +204 -0
- package/dist/ui/elements/PcPane/index.d.ts +118 -0
- package/dist/ui/elements/PcPane/index.js +357 -0
- package/dist/ui/elements/PcPanel/index.d.ts +26 -8
- package/dist/ui/elements/PcPanel/index.js +76 -8
- package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.d.ts +20 -6
- package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.js +20 -15
- package/dist/ui/elements/PcPanelBrushes/style.js +3 -0
- package/dist/ui/elements/PcPanelColours/index.d.ts +171 -0
- package/dist/ui/elements/PcPanelColours/index.js +650 -0
- package/dist/ui/elements/PcPanelColours/style.js +3 -0
- package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.d.ts +17 -4
- package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.js +17 -10
- package/dist/ui/elements/PcPanelLayers/style.js +3 -0
- package/dist/ui/elements/PcShortcutSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcShortcutSettings/index.js +273 -0
- package/dist/ui/elements/PcThemePicker/index.js +1 -0
- package/dist/ui/elements/PcToast/index.js +1 -0
- package/dist/ui/elements/PcToolSettings/index.js +2 -1
- package/dist/ui/elements/PcToolbar/index.d.ts +47 -12
- package/dist/ui/elements/PcToolbar/index.js +77 -56
- package/dist/ui/elements/PcToolbar/style.js +1 -1
- package/dist/ui/index.d.ts +7 -1
- package/dist/ui/index.js +8 -2
- package/dist/ui/tokens/index.js +3 -1
- package/locales/en-GB.json +87 -1
- package/locales/en-US.json +87 -1
- package/locales/fr.json +87 -1
- package/locales/ja.json +87 -1
- package/locales/ko.json +87 -1
- package/locales/zh-Hans.json +87 -1
- package/locales/zh-Hant.json +87 -1
- package/package.json +1 -1
- package/dist/ui/elements/PcBrushMenu/style.js +0 -3
- package/dist/ui/elements/PcLayersMenu/style.js +0 -3
|
@@ -4,14 +4,28 @@ import ChangeController from "../../ChangeController/index.js";
|
|
|
4
4
|
import PaintCanvas, { Panel } from "../../../lib/classes/PaintCanvas/index.js";
|
|
5
5
|
import KeyboardShortcuts, { SHORTCUT_EVENT } from "../../../lib/classes/KeyboardShortcuts/index.js";
|
|
6
6
|
import { isTouchDevice } from "../../../lib/classes/InputSettings/index.js";
|
|
7
|
-
import IndexedDbLayerStore from "../../../lib/storage/index.js";
|
|
7
|
+
import IndexedDbLayerStore, { IndexedDbSettingsStore } from "../../../lib/storage/index.js";
|
|
8
|
+
import SettingsAutosave, { SETTINGS_AUTOSAVE_ERROR_EVENT } from "../../../lib/classes/SettingsAutosave/index.js";
|
|
8
9
|
import { AUTOSAVE_ERROR_EVENT, AutosaveState } from "../../../lib/classes/LayerAutosave/index.js";
|
|
9
10
|
import { isOwnAuthor } from "../../../lib/brushes/authors/index.js";
|
|
10
11
|
import { renderScreenshot, saveFile, screenshotName } from "../../../lib/rendering/screenshot/index.js";
|
|
11
12
|
import { DEFAULT_SIDE_MODES, STATE_EVENT, parseSideModes, sideModeFor } from "../../layout/index.js";
|
|
12
13
|
import { hasToolSettings } from "../PcToolSettings/index.js";
|
|
14
|
+
import "../PcPanel/index.js";
|
|
15
|
+
import { PANE_COLLAPSE_EVENT, PcPane } from "../PcPane/index.js";
|
|
16
|
+
import "../PcToolbar/index.js";
|
|
17
|
+
import "../PcCanvas/index.js";
|
|
18
|
+
import "../PcCanvasMap/index.js";
|
|
19
|
+
import "../PcPanelBrushes/index.js";
|
|
20
|
+
import "../PcPanelLayers/index.js";
|
|
21
|
+
import "../PcBrushConfig/index.js";
|
|
22
|
+
import "../PcStats/index.js";
|
|
23
|
+
import "../PcPanelColours/index.js";
|
|
24
|
+
import "../PcDialog/index.js";
|
|
13
25
|
import { html, nothing, unsafeCSS } from "lit";
|
|
14
26
|
// src/ui/elements/PaintCanvas/index.ts
|
|
27
|
+
/** The sides */
|
|
28
|
+
var SIDES = ["left", "right"];
|
|
15
29
|
/** No space kept clear */
|
|
16
30
|
var NO_INSETS = {
|
|
17
31
|
top: 0,
|
|
@@ -21,29 +35,28 @@ var NO_INSETS = {
|
|
|
21
35
|
};
|
|
22
36
|
/** The choice that takes the drawing over from another window */
|
|
23
37
|
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
38
|
/**
|
|
27
39
|
* A ready-made painting app: the drawing elements arranged around the canvas, sized to its container (not the
|
|
28
40
|
* window), with slots to add or replace content:
|
|
29
41
|
*
|
|
30
42
|
* - `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`:
|
|
43
|
+
* - `left`: `<pc-toolbar>` (a pane) by default
|
|
44
|
+
* - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) by default
|
|
45
|
+
* - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
|
|
34
46
|
* - `center`: `<pc-canvas>` by default
|
|
35
47
|
* - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
|
|
36
48
|
* - `bottom`: under the canvas (empty by default)
|
|
37
49
|
* - `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)
|
|
50
|
+
* - `menu`: the app menu the toolbar's menu button opens, shown in a `<pc-panel>` (the button shows only with a menu)
|
|
39
51
|
*
|
|
40
52
|
* 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.
|
|
42
|
-
*
|
|
53
|
+
* interface (Tab key) leaves only the canvas and overlay. While a side has a `<pc-panel-layers>` (as the right side
|
|
54
|
+
* does by default), the toolbar has no layers menu button.
|
|
43
55
|
*
|
|
44
56
|
* 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
|
|
46
|
-
* the canvas; or `floating`, the
|
|
57
|
+
* rail@600"`), which the layout gives the side's `<pc-pane>`s (see `PcPane`): `docked` beside the canvas; `rail`, a
|
|
58
|
+
* column of buttons (one per panel) that open each panel over the canvas; or `floating`, the pane over the canvas as a
|
|
59
|
+
* box that collapses to a button. Give side content in a `<pc-pane>` to have it follow the side's mode.
|
|
47
60
|
*
|
|
48
61
|
* Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
|
|
49
62
|
* `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
|
|
@@ -53,10 +66,16 @@ var FOCUSABLE = "button, [href], input, select, textarea, [tabindex]:not([tabind
|
|
|
53
66
|
* reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
|
|
54
67
|
* that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
|
|
55
68
|
*
|
|
69
|
+
* With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette and
|
|
70
|
+
* colours) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when they can't be.
|
|
71
|
+
*
|
|
56
72
|
* @example
|
|
57
73
|
* html`<paint-canvas style="height: 600px"></paint-canvas>`
|
|
58
74
|
* html`<paint-canvas .app=${canvas} right-modes="docked, rail@800">
|
|
59
|
-
* <pc-
|
|
75
|
+
* <pc-pane slot="right">
|
|
76
|
+
* <pc-panel-layers .app=${canvas}></pc-panel-layers>
|
|
77
|
+
* <pc-panel icon="chat" label="Chat"><my-chat></my-chat></pc-panel>
|
|
78
|
+
* </pc-pane>
|
|
60
79
|
* </paint-canvas>`
|
|
61
80
|
*/
|
|
62
81
|
var PaintCanvasElement = class extends PcElement {
|
|
@@ -85,11 +104,13 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
85
104
|
},
|
|
86
105
|
shortcuts: { type: Boolean },
|
|
87
106
|
store: { type: String },
|
|
107
|
+
settingsStore: {
|
|
108
|
+
type: String,
|
|
109
|
+
attribute: "settings-store"
|
|
110
|
+
},
|
|
88
111
|
width: { state: true },
|
|
89
112
|
input: { state: true },
|
|
90
|
-
configuring: { state: true }
|
|
91
|
-
collapsed: { state: true },
|
|
92
|
-
openPanel: { state: true }
|
|
113
|
+
configuring: { state: true }
|
|
93
114
|
};
|
|
94
115
|
static styles = [PcElement.styles, unsafeCSS(style_default)];
|
|
95
116
|
/** Saves files (replaceable for tests) */
|
|
@@ -99,13 +120,10 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
99
120
|
ownAutosave;
|
|
100
121
|
/** The autosave whose errors are shown */
|
|
101
122
|
watchedAutosave;
|
|
123
|
+
/** Keeps the app settings under the `settings-store` name, if any */
|
|
124
|
+
ownSettingsAutosave;
|
|
102
125
|
/** Whether drawing was blocked when shortcuts were last watched */
|
|
103
126
|
blockedShortcuts = false;
|
|
104
|
-
/** The panels in each side's slot */
|
|
105
|
-
panels = {
|
|
106
|
-
left: [],
|
|
107
|
-
right: []
|
|
108
|
-
};
|
|
109
127
|
/** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
|
|
110
128
|
sideLayers = {
|
|
111
129
|
left: false,
|
|
@@ -121,12 +139,9 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
121
139
|
reported;
|
|
122
140
|
/** Space the floating sides cover over the canvas */
|
|
123
141
|
floatingInsets = NO_INSETS;
|
|
124
|
-
/** The rail button that opened the open panel, to return focus to */
|
|
125
|
-
opener;
|
|
126
142
|
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(([entry]) => {
|
|
127
143
|
this.width = Math.round(entry?.contentRect.width ?? 0);
|
|
128
144
|
}) : void 0;
|
|
129
|
-
panelObserver = new MutationObserver(() => this.requestUpdate());
|
|
130
145
|
onPointerDown = (event) => {
|
|
131
146
|
const kind = event.pointerType;
|
|
132
147
|
this.input = kind === "pen" || kind === "touch" ? kind : "mouse";
|
|
@@ -135,20 +150,15 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
135
150
|
message: this.t("paintCanvas.autosave.failed"),
|
|
136
151
|
kind: "error"
|
|
137
152
|
});
|
|
153
|
+
onSettingsAutosaveError = () => emit(this, "pc-toast", {
|
|
154
|
+
message: this.t("paintCanvas.settings.saveFailed"),
|
|
155
|
+
kind: "error"
|
|
156
|
+
});
|
|
138
157
|
onShortcut = (event) => {
|
|
139
158
|
if (event.detail.type === "save") this.saveScreenshot();
|
|
140
159
|
};
|
|
141
|
-
onDocumentPointerDown = (event) =>
|
|
142
|
-
|
|
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
|
-
};
|
|
160
|
+
onDocumentPointerDown = (event) => this.closePaletteOutside(event.composedPath());
|
|
161
|
+
onPaneCollapse = () => this.requestUpdate();
|
|
152
162
|
constructor() {
|
|
153
163
|
super();
|
|
154
164
|
this.leftModes = DEFAULT_SIDE_MODES;
|
|
@@ -158,10 +168,6 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
158
168
|
this.shortcuts = true;
|
|
159
169
|
this.width = 0;
|
|
160
170
|
this.input = isTouchDevice() ? "touch" : "mouse";
|
|
161
|
-
this.collapsed = {
|
|
162
|
-
left: true,
|
|
163
|
-
right: true
|
|
164
|
-
};
|
|
165
171
|
}
|
|
166
172
|
/** Whether another window is drawing on the stored drawing, so this one is blocked */
|
|
167
173
|
get blocked() {
|
|
@@ -186,36 +192,25 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
186
192
|
this.app ??= new PaintCanvas();
|
|
187
193
|
super.connectedCallback();
|
|
188
194
|
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
195
|
this.addEventListener("pointerdown", this.onPointerDown, true);
|
|
199
|
-
this.addEventListener(
|
|
196
|
+
this.addEventListener(PANE_COLLAPSE_EVENT, this.onPaneCollapse);
|
|
200
197
|
document.addEventListener("pointerdown", this.onDocumentPointerDown, true);
|
|
201
198
|
this.watchShortcuts();
|
|
202
199
|
}
|
|
203
200
|
disconnectedCallback() {
|
|
204
201
|
super.disconnectedCallback();
|
|
205
202
|
this.resizeObserver?.disconnect();
|
|
206
|
-
this.panelObserver.disconnect();
|
|
207
203
|
this.removeEventListener("pointerdown", this.onPointerDown, true);
|
|
208
|
-
this.removeEventListener(
|
|
204
|
+
this.removeEventListener(PANE_COLLAPSE_EVENT, this.onPaneCollapse);
|
|
209
205
|
document.removeEventListener("pointerdown", this.onDocumentPointerDown, true);
|
|
210
206
|
this.unwatchShortcuts();
|
|
211
207
|
}
|
|
212
208
|
willUpdate(changed) {
|
|
213
209
|
super.willUpdate(changed);
|
|
214
210
|
if (changed.has("app") || changed.has("store")) this.useStore(changed.get("app"));
|
|
211
|
+
if (changed.has("app") || changed.has("settingsStore")) this.useSettingsStore();
|
|
215
212
|
this.watchAutosaveErrors();
|
|
216
213
|
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
214
|
}
|
|
220
215
|
updated() {
|
|
221
216
|
const state = this.state;
|
|
@@ -229,11 +224,25 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
229
224
|
this.reported = report;
|
|
230
225
|
this.dispatchEvent(new CustomEvent(STATE_EVENT, { detail: state }));
|
|
231
226
|
}
|
|
227
|
+
this.setPaneModes();
|
|
232
228
|
this.measureFloatingSides();
|
|
233
229
|
}
|
|
230
|
+
/** The panes in a side: the default one, or those given for it */
|
|
231
|
+
panesIn(side) {
|
|
232
|
+
const slot = this.renderRoot.querySelector(`slot[name="${side}"]`);
|
|
233
|
+
const given = slot.assignedElements();
|
|
234
|
+
return (given.length ? given : [...slot.children]).filter((element) => element instanceof PcPane);
|
|
235
|
+
}
|
|
236
|
+
/** Show each side's panes in the side's mode, facing the canvas */
|
|
237
|
+
setPaneModes() {
|
|
238
|
+
for (const side of SIDES) for (const pane of this.panesIn(side)) {
|
|
239
|
+
pane.mode = this.modeOf(side);
|
|
240
|
+
pane.edge = side;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
234
243
|
/** Keep the canvas clear of floating sides when fitting it */
|
|
235
244
|
measureFloatingSides() {
|
|
236
|
-
const width = (side) => this.
|
|
245
|
+
const width = (side) => this.panesIn(side).filter((pane) => pane.mode === "floating" && !pane.collapsed).reduce((widest, pane) => Math.max(widest, pane.getBoundingClientRect().width), 0);
|
|
237
246
|
const insets = {
|
|
238
247
|
...NO_INSETS,
|
|
239
248
|
left: width("left"),
|
|
@@ -258,6 +267,20 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
258
267
|
}
|
|
259
268
|
if (app.autosave?.store.name !== name) this.ownAutosave = app.useLayerStore(new IndexedDbLayerStore(name));
|
|
260
269
|
}
|
|
270
|
+
/** Keeps the app settings under the `settings-store` name, if any (e.g. to wait until they are restored with `ready`) */
|
|
271
|
+
get settingsAutosave() {
|
|
272
|
+
return this.ownSettingsAutosave;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Keep the canvas's app settings under the `settings-store` name, stopping the previous settings autosave (which
|
|
276
|
+
* saves any change waiting to be saved).
|
|
277
|
+
*/
|
|
278
|
+
useSettingsStore() {
|
|
279
|
+
this.ownSettingsAutosave?.stop();
|
|
280
|
+
const name = this.settingsStore || void 0;
|
|
281
|
+
this.ownSettingsAutosave = name === void 0 ? void 0 : new SettingsAutosave(this.app, new IndexedDbSettingsStore(name));
|
|
282
|
+
this.ownSettingsAutosave?.addEventListener(SETTINGS_AUTOSAVE_ERROR_EVENT, this.onSettingsAutosaveError);
|
|
283
|
+
}
|
|
261
284
|
/** Show errors from the canvas's current autosave */
|
|
262
285
|
watchAutosaveErrors() {
|
|
263
286
|
const autosave = this.app?.autosave;
|
|
@@ -330,40 +353,6 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
330
353
|
}
|
|
331
354
|
};
|
|
332
355
|
}
|
|
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
356
|
/** Follow which slots have content given (their defaults aren't needed) */
|
|
368
357
|
onSlotChange(event) {
|
|
369
358
|
const slot = event.target;
|
|
@@ -379,16 +368,12 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
379
368
|
>${this.filled[name] ? nothing : fallback}</slot
|
|
380
369
|
>`;
|
|
381
370
|
}
|
|
382
|
-
/** Follow
|
|
371
|
+
/** Follow whether a side given content has a layers menu */
|
|
383
372
|
onSideSlotChange(side, event) {
|
|
384
373
|
const assigned = event.target.assignedElements();
|
|
385
|
-
this.panels = {
|
|
386
|
-
...this.panels,
|
|
387
|
-
[side]: assigned.filter((element) => element.localName === "pc-panel")
|
|
388
|
-
};
|
|
389
374
|
this.sideLayers = {
|
|
390
375
|
...this.sideLayers,
|
|
391
|
-
[side]: assigned.some((element) => element.matches("pc-layers
|
|
376
|
+
[side]: assigned.some((element) => element.matches("pc-panel-layers, :has(pc-panel-layers)"))
|
|
392
377
|
};
|
|
393
378
|
this.onSlotChange(event);
|
|
394
379
|
}
|
|
@@ -399,99 +384,70 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
399
384
|
}
|
|
400
385
|
/** Whether a side has a layers menu, replacing the toolbar's */
|
|
401
386
|
get layersInSide() {
|
|
402
|
-
return this.sideLayers.left || this.sideLayers.right;
|
|
387
|
+
return this.sideLayers.left || this.sideLayers.right || !this.filled.right;
|
|
403
388
|
}
|
|
404
|
-
/**
|
|
405
|
-
|
|
406
|
-
return html`<pc-toolbar
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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("paintCanvas.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 */
|
|
389
|
+
/** A side's default content: the toolbar on the left, and the layers on the right */
|
|
390
|
+
renderSideDefault(side, app, label) {
|
|
391
|
+
if (side === "left") return html`<pc-toolbar
|
|
392
|
+
.app=${app}
|
|
393
|
+
.menu=${this.hasMenu}
|
|
394
|
+
.layers=${!this.layersInSide}
|
|
395
|
+
.menuText=${this.menuText}
|
|
396
|
+
.menuLabel=${this.menuLabel}
|
|
397
|
+
label=${label}
|
|
398
|
+
></pc-toolbar>`;
|
|
399
|
+
return html`<pc-pane label=${label}
|
|
400
|
+
><pc-panel-layers slot="start" .app=${app}></pc-panel-layers
|
|
401
|
+
></pc-pane>`;
|
|
402
|
+
}
|
|
403
|
+
/** A side: its panes, in its mode (set on them after rendering) */
|
|
438
404
|
renderSide(side, app, blocked) {
|
|
439
|
-
const mode = this.modeOf(side);
|
|
440
405
|
const label = this.t(side === "left" ? "paintCanvas.layout.tools" : "paintCanvas.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 ? "paintCanvas.layout.show" : "paintCanvas.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
406
|
return html`<div
|
|
471
407
|
class="side ${side}"
|
|
472
408
|
part=${side}
|
|
473
|
-
data-mode=${
|
|
409
|
+
data-mode=${this.modeOf(side)}
|
|
474
410
|
role="region"
|
|
475
411
|
aria-label=${label}
|
|
476
412
|
?inert=${blocked}
|
|
477
413
|
>
|
|
478
|
-
|
|
414
|
+
<slot
|
|
415
|
+
name=${side}
|
|
416
|
+
@slotchange=${(event) => this.onSideSlotChange(side, event)}
|
|
417
|
+
>${this.filled[side] ? nothing : this.renderSideDefault(side, app, label)}</slot
|
|
418
|
+
>
|
|
479
419
|
</div>`;
|
|
480
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Close the colour palette on a press outside it and the colours that open it.
|
|
423
|
+
*/
|
|
424
|
+
closePaletteOutside(path) {
|
|
425
|
+
const app = this.app;
|
|
426
|
+
if (app?.panel !== Panel.Palette) return;
|
|
427
|
+
if (!path.some((target) => target instanceof Element && (target.localName === "pc-panel-colours" || target.localName === "pc-colour-pair"))) app.closePanel();
|
|
428
|
+
}
|
|
481
429
|
/** The menus the toolbar opens, floating over the canvas beside it */
|
|
482
430
|
renderPanels(app) {
|
|
483
|
-
const brushMenu = (kind) => html`<pc-
|
|
431
|
+
const brushMenu = (kind) => html`<pc-panel-brushes
|
|
484
432
|
class="panel top-panel"
|
|
485
433
|
kind=${kind}
|
|
486
434
|
.app=${app}
|
|
487
435
|
@pc-new-preset=${() => this.newPreset(kind)}
|
|
488
|
-
></pc-
|
|
436
|
+
></pc-panel-brushes>`;
|
|
489
437
|
return html`${app.panel === Panel.Brush ? brushMenu("brush") : nothing}
|
|
490
438
|
${app.panel === Panel.Eraser ? brushMenu("eraser") : nothing}
|
|
491
|
-
${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-layers
|
|
492
|
-
|
|
439
|
+
${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-panel-layers class="panel" .app=${app}></pc-panel-layers>` : nothing}
|
|
440
|
+
${app.panel === Panel.Palette ? html`<pc-panel-colours
|
|
441
|
+
class="panel top-panel"
|
|
442
|
+
.app=${app}
|
|
443
|
+
></pc-panel-colours>` : nothing}
|
|
444
|
+
<pc-panel
|
|
445
|
+
class="panel menu-panel"
|
|
446
|
+
label=${this.menuLabel || this.t("paintCanvas.toolbar.menu")}
|
|
447
|
+
?open=${app.panel === Panel.Menu}
|
|
448
|
+
>
|
|
493
449
|
<slot name="menu" @slotchange=${this.onMenuSlotChange}></slot>
|
|
494
|
-
</
|
|
450
|
+
</pc-panel>`;
|
|
495
451
|
}
|
|
496
452
|
/** The brush configuration window, while open */
|
|
497
453
|
renderConfig(app) {
|
|
@@ -540,10 +496,13 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
540
496
|
aria-label=${this.t("paintCanvas.layout.toolSettings")}
|
|
541
497
|
?inert=${blocked}
|
|
542
498
|
>
|
|
543
|
-
${this.slotWithDefault("top", showsToolSettings ? html`<pc-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
499
|
+
${this.slotWithDefault("top", showsToolSettings ? html`<pc-pane orientation="horizontal" edge="top"
|
|
500
|
+
><pc-tool-settings
|
|
501
|
+
slot="middle"
|
|
502
|
+
.app=${app}
|
|
503
|
+
@pc-configure-brush=${(event) => this.configure(event.detail)}
|
|
504
|
+
></pc-tool-settings
|
|
505
|
+
></pc-pane>` : nothing)}
|
|
547
506
|
</div>
|
|
548
507
|
<div
|
|
549
508
|
class="center"
|
|
@@ -1,3 +1,3 @@
|
|
|
1
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
|
|
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/* The tool settings fill the top pane */\n.top pc-tool-settings {\n flex: 1;\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/* A side's panes fill it */\n.side pc-pane,\n.side pc-toolbar,\n.side ::slotted(*) {\n flex: 1;\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/* The app menu: the implementer's content in a panel */\n.menu-panel:not([open]) {\n display: none;\n}\n\n/* Floating: the side's panes float over the canvas (see pc-pane), so the side takes no space */\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/* While another window is drawing, its message covers only the layout (not the whole page) */\npc-dialog.elsewhere {\n position: absolute;\n}\n";
|
|
3
3
|
export { style_default as default };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { nothing, type PropertyValues } from "lit";
|
|
2
|
+
import { PcElement } from "../../base";
|
|
3
|
+
import "../PcShortcutSettings";
|
|
4
|
+
import type PaintCanvas from "../../../lib/classes/PaintCanvas";
|
|
5
|
+
import { type SaveMethod } from "../../../lib/rendering/screenshot";
|
|
6
|
+
/**
|
|
7
|
+
* Buttons for the user's app settings: "Keyboard shortcuts", which opens a window to change them (see
|
|
8
|
+
* `pc-shortcut-settings`), and "Import settings" and "Export settings", which read and write a settings JSON file (input
|
|
9
|
+
* settings, keyboard shortcuts, colour palette and colours). The window is a modal `<dialog>` above the whole page; it
|
|
10
|
+
* closes with its Close button, Escape, or a press on the backdrop, and the browser gives focus back to the button. Fires `pc-toast` after importing, or when importing or
|
|
11
|
+
* exporting fails.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* html`<pc-app-settings .app=${canvas}></pc-app-settings>`
|
|
15
|
+
*/
|
|
16
|
+
export declare class PcAppSettings extends PcElement {
|
|
17
|
+
static properties: {
|
|
18
|
+
app: {
|
|
19
|
+
attribute: boolean;
|
|
20
|
+
};
|
|
21
|
+
shortcutsOpen: {
|
|
22
|
+
type: BooleanConstructor;
|
|
23
|
+
attribute: string;
|
|
24
|
+
};
|
|
25
|
+
translations: {
|
|
26
|
+
attribute: boolean;
|
|
27
|
+
};
|
|
28
|
+
icons: {
|
|
29
|
+
attribute: boolean;
|
|
30
|
+
};
|
|
31
|
+
theme: {
|
|
32
|
+
type: StringConstructor;
|
|
33
|
+
reflect: boolean;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
static styles: import("lit").CSSResultGroup[];
|
|
37
|
+
app?: PaintCanvas;
|
|
38
|
+
/** Whether the keyboard shortcuts window is open */
|
|
39
|
+
shortcutsOpen: boolean;
|
|
40
|
+
/** Saves exported files (replaceable for tests) */
|
|
41
|
+
saveFile: (blob: Blob, name: string) => Promise<SaveMethod | undefined>;
|
|
42
|
+
constructor();
|
|
43
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
44
|
+
/** Close the keyboard shortcuts window */
|
|
45
|
+
closeShortcuts(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Import the settings in a settings file, using the parts it has.
|
|
48
|
+
*/
|
|
49
|
+
importFile(file: File): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Export the settings as a settings file.
|
|
52
|
+
*/
|
|
53
|
+
exportFile(): Promise<void>;
|
|
54
|
+
/** Show a settings file error (one of `SETTINGS_FILE_ERRORS`) in the chosen language */
|
|
55
|
+
private toastError;
|
|
56
|
+
/** The keyboard shortcuts window: a modal dialog, with its contents only while open */
|
|
57
|
+
private renderShortcuts;
|
|
58
|
+
render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
59
|
+
}
|