@lvl99dotcom/paintcanvas 0.3.1 → 0.5.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.
Files changed (212) hide show
  1. package/README.md +30 -15
  2. package/dist/lib/brushes/StrokeEngine/index.d.ts +2 -2
  3. package/dist/lib/brushes/StrokeEngine/index.js +26 -28
  4. package/dist/lib/brushes/presets/round/index.d.ts +1 -1
  5. package/dist/lib/brushes/presets/round/index.js +12 -7
  6. package/dist/lib/brushes/presets/square/index.d.ts +1 -1
  7. package/dist/lib/brushes/presets/square/index.js +7 -3
  8. package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
  9. package/dist/lib/brushes/shapeImage/index.js +15 -9
  10. package/dist/lib/brushes/shapes/index.d.ts +18 -0
  11. package/dist/lib/brushes/shapes/index.js +86 -0
  12. package/dist/lib/classes/BrushSettings/index.d.ts +53 -2
  13. package/dist/lib/classes/BrushSettings/index.js +85 -14
  14. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +47 -7
  15. package/dist/lib/classes/KeyboardShortcuts/index.js +145 -20
  16. package/dist/lib/classes/PaintCanvas/index.d.ts +123 -27
  17. package/dist/lib/classes/PaintCanvas/index.js +208 -41
  18. package/dist/lib/classes/PaintCanvas/tool.d.ts +15 -0
  19. package/dist/lib/classes/PaintCanvas/tool.js +18 -0
  20. package/dist/lib/classes/Palette/index.d.ts +58 -0
  21. package/dist/lib/classes/Palette/index.js +92 -0
  22. package/dist/lib/classes/SettingsAutosave/index.d.ts +53 -0
  23. package/dist/lib/classes/SettingsAutosave/index.js +98 -0
  24. package/dist/lib/classes/ShortcutSettings/index.d.ts +63 -0
  25. package/dist/lib/classes/ShortcutSettings/index.js +110 -0
  26. package/dist/lib/classes/UiSettings/index.d.ts +68 -0
  27. package/dist/lib/classes/UiSettings/index.js +82 -0
  28. package/dist/lib/palette/index.d.ts +49 -0
  29. package/dist/lib/palette/index.js +82 -0
  30. package/dist/lib/palette/schema.d.ts +45 -0
  31. package/dist/lib/palette/schema.js +41 -0
  32. package/dist/lib/rendering/cursors/index.d.ts +2 -0
  33. package/dist/lib/rendering/cursors/index.js +3 -1
  34. package/dist/lib/rendering/sample/index.js +3 -2
  35. package/dist/lib/settings/index.d.ts +63 -0
  36. package/dist/lib/settings/index.js +93 -0
  37. package/dist/lib/settings/schema.d.ts +168 -0
  38. package/dist/lib/settings/schema.js +116 -0
  39. package/dist/lib/storage/index.d.ts +36 -5
  40. package/dist/lib/storage/index.js +87 -29
  41. package/dist/lib/utils/colour/index.d.ts +72 -0
  42. package/dist/lib/utils/colour/index.js +154 -0
  43. package/dist/lib/utils/shortcuts/index.d.ts +174 -11
  44. package/dist/lib/utils/shortcuts/index.js +391 -145
  45. package/dist/lib/validation/validators.generated.d.ts +5 -0
  46. package/dist/lib/validation/validators.generated.js +1040 -1
  47. package/dist/locales/en-GB.js +96 -3
  48. package/dist/locales/en-US.js +96 -3
  49. package/dist/locales/fr.js +96 -3
  50. package/dist/locales/ja.js +96 -3
  51. package/dist/locales/ko.js +96 -3
  52. package/dist/locales/zh-Hans.js +96 -3
  53. package/dist/locales/zh-Hant.js +96 -3
  54. package/dist/package.js +1 -1
  55. package/dist/testing/recordingCanvas.js +2 -1
  56. package/dist/ui/base/index.d.ts +2 -2
  57. package/dist/ui/dismiss/index.d.ts +21 -0
  58. package/dist/ui/dismiss/index.js +27 -0
  59. package/dist/ui/drag/index.d.ts +51 -0
  60. package/dist/ui/drag/index.js +81 -0
  61. package/dist/ui/elements/PaintCanvas/index.d.ts +57 -50
  62. package/dist/ui/elements/PaintCanvas/index.js +141 -174
  63. package/dist/ui/elements/PaintCanvas/style.js +1 -1
  64. package/dist/ui/elements/PcAppSettings/index.d.ts +59 -0
  65. package/dist/ui/elements/PcAppSettings/index.js +222 -0
  66. package/dist/ui/elements/PcBrushConfig/index.d.ts +9 -2
  67. package/dist/ui/elements/PcBrushConfig/index.js +47 -7
  68. package/dist/ui/elements/PcBrushConfig/style.js +1 -1
  69. package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
  70. package/dist/ui/elements/PcBrushSize/index.js +66 -0
  71. package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
  72. package/dist/ui/elements/PcBrushTest/index.js +73 -5
  73. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  74. package/dist/ui/elements/PcCanvas/index.d.ts +22 -2
  75. package/dist/ui/elements/PcCanvas/index.js +60 -15
  76. package/dist/ui/elements/PcCanvasMap/style.js +1 -1
  77. package/dist/ui/elements/PcClipboardTools/index.d.ts +48 -0
  78. package/dist/ui/elements/PcClipboardTools/index.js +118 -0
  79. package/dist/ui/elements/PcColourPair/index.d.ts +43 -0
  80. package/dist/ui/elements/PcColourPair/index.js +125 -0
  81. package/dist/ui/elements/PcContextMenu/index.d.ts +71 -34
  82. package/dist/ui/elements/PcContextMenu/index.js +128 -97
  83. package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
  84. package/dist/ui/elements/PcInputSettings/index.js +16 -1
  85. package/dist/ui/elements/PcLayerItem/index.d.ts +13 -34
  86. package/dist/ui/elements/PcLayerItem/index.js +39 -91
  87. package/dist/ui/elements/PcLayerItem/style.js +1 -1
  88. package/dist/ui/elements/PcMenu/index.d.ts +138 -0
  89. package/dist/ui/elements/PcMenu/index.js +296 -0
  90. package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
  91. package/dist/ui/elements/PcNumberSlider/index.js +247 -0
  92. package/dist/ui/elements/PcPane/index.d.ts +120 -0
  93. package/dist/ui/elements/PcPane/index.js +394 -0
  94. package/dist/ui/elements/PcPanel/index.d.ts +26 -8
  95. package/dist/ui/elements/PcPanel/index.js +76 -8
  96. package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.d.ts +22 -21
  97. package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.js +24 -57
  98. package/dist/ui/elements/PcPanelBrushes/style.js +3 -0
  99. package/dist/ui/elements/PcPanelColours/index.d.ts +171 -0
  100. package/dist/ui/elements/PcPanelColours/index.js +650 -0
  101. package/dist/ui/elements/PcPanelColours/style.js +3 -0
  102. package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.d.ts +17 -4
  103. package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.js +17 -10
  104. package/dist/ui/elements/PcPanelLayers/style.js +3 -0
  105. package/dist/ui/elements/PcPopover/index.d.ts +39 -1
  106. package/dist/ui/elements/PcPopover/index.js +72 -4
  107. package/dist/ui/elements/PcShortcutSettings/index.d.ts +69 -0
  108. package/dist/ui/elements/PcShortcutSettings/index.js +273 -0
  109. package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
  110. package/dist/ui/elements/PcThemePicker/index.js +22 -21
  111. package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
  112. package/dist/ui/elements/PcToolSettings/index.js +384 -319
  113. package/dist/ui/elements/PcToolSettings/style.js +1 -1
  114. package/dist/ui/elements/PcToolbar/index.d.ts +62 -12
  115. package/dist/ui/elements/PcToolbar/index.js +119 -57
  116. package/dist/ui/elements/PcToolbar/style.js +1 -1
  117. package/dist/ui/icons/images/icon-brush@2x.js +3 -0
  118. package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
  119. package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
  120. package/dist/ui/icons/images/icon-cog@2x.js +3 -0
  121. package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
  122. package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
  123. package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
  124. package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
  125. package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
  126. package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
  127. package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
  128. package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
  129. package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
  130. package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
  131. package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
  132. package/dist/ui/icons/images/icon-layers@2x.js +3 -0
  133. package/dist/ui/icons/images/icon-linked@2x.js +3 -0
  134. package/dist/ui/icons/images/icon-locked@2x.js +3 -0
  135. package/dist/ui/icons/images/icon-moon@2x.js +3 -0
  136. package/dist/ui/icons/images/icon-pan@2x.js +3 -0
  137. package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
  138. package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
  139. package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
  140. package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
  141. package/dist/ui/icons/images/icon-redo@2x.js +3 -0
  142. package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
  143. package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
  144. package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
  145. package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
  146. package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
  147. package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
  148. package/dist/ui/icons/images/icon-star@2x.js +3 -0
  149. package/dist/ui/icons/images/icon-sun@2x.js +3 -0
  150. package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
  151. package/dist/ui/icons/images/icon-undo@2x.js +3 -0
  152. package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
  153. package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
  154. package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
  155. package/dist/ui/icons/images/icon-visible@2x.js +3 -0
  156. package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
  157. package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
  158. package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
  159. package/dist/ui/icons/index.d.ts +1 -1
  160. package/dist/ui/icons/index.js +84 -72
  161. package/dist/ui/index.d.ts +13 -2
  162. package/dist/ui/index.js +16 -4
  163. package/dist/ui/layout/index.d.ts +9 -1
  164. package/dist/ui/layout/index.js +11 -2
  165. package/dist/ui/tokens/index.d.ts +5 -4
  166. package/dist/ui/tokens/index.js +15 -8
  167. package/locales/en-GB.json +96 -3
  168. package/locales/en-US.json +96 -3
  169. package/locales/fr.json +96 -3
  170. package/locales/ja.json +96 -3
  171. package/locales/ko.json +96 -3
  172. package/locales/zh-Hans.json +96 -3
  173. package/locales/zh-Hant.json +96 -3
  174. package/package.json +11 -4
  175. package/dist/ui/elements/PcBrushMenu/style.js +0 -3
  176. package/dist/ui/elements/PcLayersMenu/style.js +0 -3
  177. package/dist/ui/icons/images/brush.js +0 -3
  178. package/dist/ui/icons/images/circle-mask.js +0 -3
  179. package/dist/ui/icons/images/clipboard.js +0 -3
  180. package/dist/ui/icons/images/cursor.js +0 -3
  181. package/dist/ui/icons/images/eraser.js +0 -3
  182. package/dist/ui/icons/images/eyedropper.js +0 -3
  183. package/dist/ui/icons/images/flip-horizontal.js +0 -3
  184. package/dist/ui/icons/images/flip-vertical.js +0 -3
  185. package/dist/ui/icons/images/focus-pen.js +0 -3
  186. package/dist/ui/icons/images/freehand-mask.js +0 -3
  187. package/dist/ui/icons/images/hidden.js +0 -3
  188. package/dist/ui/icons/images/layers.js +0 -3
  189. package/dist/ui/icons/images/linked.js +0 -3
  190. package/dist/ui/icons/images/locked.js +0 -3
  191. package/dist/ui/icons/images/moon.js +0 -3
  192. package/dist/ui/icons/images/pan.js +0 -3
  193. package/dist/ui/icons/images/polygon-mask.js +0 -3
  194. package/dist/ui/icons/images/pressure.js +0 -3
  195. package/dist/ui/icons/images/randomise.js +0 -3
  196. package/dist/ui/icons/images/rectangle-mask.js +0 -3
  197. package/dist/ui/icons/images/redo.js +0 -3
  198. package/dist/ui/icons/images/rotate-90-left.js +0 -3
  199. package/dist/ui/icons/images/rotate-90-right.js +0 -3
  200. package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
  201. package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
  202. package/dist/ui/icons/images/smoothing-linear.js +0 -3
  203. package/dist/ui/icons/images/star.js +0 -3
  204. package/dist/ui/icons/images/sun.js +0 -3
  205. package/dist/ui/icons/images/tilt.js +0 -3
  206. package/dist/ui/icons/images/undo.js +0 -3
  207. package/dist/ui/icons/images/unlinked.js +0 -3
  208. package/dist/ui/icons/images/unlocked.js +0 -3
  209. package/dist/ui/icons/images/visible.js +0 -3
  210. package/dist/ui/icons/images/zoom-in.js +0 -3
  211. package/dist/ui/icons/images/zoom-out.js +0 -3
  212. package/dist/ui/icons/images/zoom.js +0 -3
@@ -1,26 +1,33 @@
1
1
  import style_default from "./style.js";
2
2
  import { PcElement, defineElement, emit } from "../../base/index.js";
3
+ import { markDismissingPress } from "../../dismiss/index.js";
3
4
  import ChangeController from "../../ChangeController/index.js";
4
5
  import PaintCanvas, { Panel } from "../../../lib/classes/PaintCanvas/index.js";
5
6
  import KeyboardShortcuts, { SHORTCUT_EVENT } from "../../../lib/classes/KeyboardShortcuts/index.js";
6
7
  import { isTouchDevice } from "../../../lib/classes/InputSettings/index.js";
7
- import IndexedDbLayerStore from "../../../lib/storage/index.js";
8
+ import IndexedDbLayerStore, { IndexedDbSettingsStore } from "../../../lib/storage/index.js";
9
+ import SettingsAutosave, { SETTINGS_AUTOSAVE_ERROR_EVENT } from "../../../lib/classes/SettingsAutosave/index.js";
8
10
  import { AUTOSAVE_ERROR_EVENT, AutosaveState } from "../../../lib/classes/LayerAutosave/index.js";
9
11
  import { isOwnAuthor } from "../../../lib/brushes/authors/index.js";
10
12
  import { renderScreenshot, saveFile, screenshotName } from "../../../lib/rendering/screenshot/index.js";
11
13
  import { DEFAULT_SIDE_MODES, STATE_EVENT, parseSideModes, sideModeFor } from "../../layout/index.js";
12
14
  import { hasToolSettings } from "../PcToolSettings/index.js";
13
15
  import "../PcPanel/index.js";
14
- import "../PcToolbar/index.js";
16
+ import { PANE_COLLAPSE_EVENT, PcPane } from "../PcPane/index.js";
17
+ import { PcToolbar } from "../PcToolbar/index.js";
18
+ import "../PcClipboardTools/index.js";
15
19
  import "../PcCanvas/index.js";
16
20
  import "../PcCanvasMap/index.js";
17
- import "../PcBrushMenu/index.js";
18
- import "../PcLayersMenu/index.js";
21
+ import "../PcPanelBrushes/index.js";
22
+ import "../PcPanelLayers/index.js";
19
23
  import "../PcBrushConfig/index.js";
20
24
  import "../PcStats/index.js";
25
+ import "../PcPanelColours/index.js";
21
26
  import "../PcDialog/index.js";
22
27
  import { html, nothing, unsafeCSS } from "lit";
23
28
  // src/ui/elements/PaintCanvas/index.ts
29
+ /** The sides */
30
+ var SIDES = ["left", "right"];
24
31
  /** No space kept clear */
25
32
  var NO_INSETS = {
26
33
  top: 0,
@@ -30,29 +37,31 @@ var NO_INSETS = {
30
37
  };
31
38
  /** The choice that takes the drawing over from another window */
32
39
  var TAKE_OVER_CHOICE = "take-over";
33
- /** Elements that can take keyboard focus */
34
- var FOCUSABLE = "button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])";
35
40
  /**
36
41
  * A ready-made painting app: the drawing elements arranged around the canvas, sized to its container (not the
37
42
  * window), with slots to add or replace content:
38
43
  *
39
44
  * - `header`: across the top (empty by default)
40
- * - `left`: `<pc-toolbar>` by default
41
- * - `top`: `<pc-tool-settings>` (for tools that have settings) by default
42
- * - `right`: empty by default (e.g. a `<pc-panel>` with a layers menu or chat)
45
+ * - `left`: `<pc-toolbar>` (a pane) by default; toolbars in the sides are minimised (their tools one button that opens a
46
+ * menu of them) while the layout is narrow
47
+ * - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) and, at its end,
48
+ * `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow) by default
49
+ * - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
43
50
  * - `center`: `<pc-canvas>` by default
44
51
  * - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
45
52
  * - `bottom`: under the canvas (empty by default)
46
53
  * - `footer`: across the bottom; `<pc-stats>` by default
47
- * - `menu`: the app menu the toolbar's menu button opens (the button shows only with a menu)
54
+ * - `menu`: the app menu the toolbar's menu button opens, shown in a `<pc-panel>` (the button shows only with a menu)
48
55
  *
49
56
  * Content given for a slot replaces its default; an empty element clears it. Empty slots take no space. Hiding the
50
- * interface (Tab key) leaves only the canvas and overlay. A `<pc-layers-menu>` given in a side (e.g. in a `<pc-panel>`)
51
- * replaces the toolbar's layers menu button.
57
+ * interface (Tab key) leaves only the canvas and overlay. While a side has a `<pc-panel-layers>` (as the right side
58
+ * does by default), the toolbar has no layers menu button.
52
59
  *
53
60
  * Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
54
- * rail@600"`): `docked` beside the canvas; `rail`, a column of buttons (one per `<pc-panel>`) that open each panel over
55
- * the canvas; or `floating`, the side over the canvas as a palette that collapses to a button.
61
+ * rail@600"`), which the layout gives the side's `<pc-pane>`s (see `PcPane`): `docked` beside the canvas; `rail`, a
62
+ * column of buttons (one per panel) that open each panel over the canvas; `floating`, the pane over the canvas as a
63
+ * box that collapses to a button; or `overlay`, a rail over the canvas without a background (the canvas fills the
64
+ * width behind it). Give side content in a `<pc-pane>` to have it follow the side's mode.
56
65
  *
57
66
  * Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
58
67
  * `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
@@ -62,10 +71,16 @@ var FOCUSABLE = "button, [href], input, select, textarea, [tabindex]:not([tabind
62
71
  * reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
63
72
  * that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
64
73
  *
74
+ * With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette, colours,
75
+ * theme and language) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when they can't be.
76
+ *
65
77
  * @example
66
78
  * html`<paint-canvas style="height: 600px"></paint-canvas>`
67
79
  * html`<paint-canvas .app=${canvas} right-modes="docked, rail@800">
68
- * <pc-panel slot="right" icon="layers" label="Layers"><pc-layers-menu .app=${canvas}></pc-layers-menu></pc-panel>
80
+ * <pc-pane slot="right">
81
+ * <pc-panel-layers .app=${canvas}></pc-panel-layers>
82
+ * <pc-panel icon="chat" label="Chat"><my-chat></my-chat></pc-panel>
83
+ * </pc-pane>
69
84
  * </paint-canvas>`
70
85
  */
71
86
  var PaintCanvasElement = class extends PcElement {
@@ -94,11 +109,13 @@ var PaintCanvasElement = class extends PcElement {
94
109
  },
95
110
  shortcuts: { type: Boolean },
96
111
  store: { type: String },
112
+ settingsStore: {
113
+ type: String,
114
+ attribute: "settings-store"
115
+ },
97
116
  width: { state: true },
98
117
  input: { state: true },
99
- configuring: { state: true },
100
- collapsed: { state: true },
101
- openPanel: { state: true }
118
+ configuring: { state: true }
102
119
  };
103
120
  static styles = [PcElement.styles, unsafeCSS(style_default)];
104
121
  /** Saves files (replaceable for tests) */
@@ -108,13 +125,10 @@ var PaintCanvasElement = class extends PcElement {
108
125
  ownAutosave;
109
126
  /** The autosave whose errors are shown */
110
127
  watchedAutosave;
128
+ /** Keeps the app settings under the `settings-store` name, if any */
129
+ ownSettingsAutosave;
111
130
  /** Whether drawing was blocked when shortcuts were last watched */
112
131
  blockedShortcuts = false;
113
- /** The panels in each side's slot */
114
- panels = {
115
- left: [],
116
- right: []
117
- };
118
132
  /** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
119
133
  sideLayers = {
120
134
  left: false,
@@ -128,14 +142,11 @@ var PaintCanvasElement = class extends PcElement {
128
142
  keys;
129
143
  /** The state last reported, to report only changes */
130
144
  reported;
131
- /** Space the floating sides cover over the canvas */
145
+ /** Space the floating and overlay sides cover over the canvas */
132
146
  floatingInsets = NO_INSETS;
133
- /** The rail button that opened the open panel, to return focus to */
134
- opener;
135
147
  resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(([entry]) => {
136
148
  this.width = Math.round(entry?.contentRect.width ?? 0);
137
149
  }) : void 0;
138
- panelObserver = new MutationObserver(() => this.requestUpdate());
139
150
  onPointerDown = (event) => {
140
151
  const kind = event.pointerType;
141
152
  this.input = kind === "pen" || kind === "touch" ? kind : "mouse";
@@ -144,20 +155,15 @@ var PaintCanvasElement = class extends PcElement {
144
155
  message: this.t("paintCanvas.autosave.failed"),
145
156
  kind: "error"
146
157
  });
158
+ onSettingsAutosaveError = () => emit(this, "pc-toast", {
159
+ message: this.t("paintCanvas.settings.saveFailed"),
160
+ kind: "error"
161
+ });
147
162
  onShortcut = (event) => {
148
163
  if (event.detail.type === "save") this.saveScreenshot();
149
164
  };
150
- onDocumentPointerDown = (event) => {
151
- const panel = this.openPanel;
152
- if (!panel) return;
153
- const path = event.composedPath();
154
- if (!path.includes(panel) && !(this.opener && path.includes(this.opener))) this.closePanel(false);
155
- };
156
- onKeyDown = (event) => {
157
- if (event.key !== "Escape" || !this.openPanel) return;
158
- event.preventDefault();
159
- this.closePanel(true);
160
- };
165
+ onDocumentPointerDown = (event) => this.closePanelOutside(event);
166
+ onPaneCollapse = () => this.requestUpdate();
161
167
  constructor() {
162
168
  super();
163
169
  this.leftModes = DEFAULT_SIDE_MODES;
@@ -167,10 +173,6 @@ var PaintCanvasElement = class extends PcElement {
167
173
  this.shortcuts = true;
168
174
  this.width = 0;
169
175
  this.input = isTouchDevice() ? "touch" : "mouse";
170
- this.collapsed = {
171
- left: true,
172
- right: true
173
- };
174
176
  }
175
177
  /** Whether another window is drawing on the stored drawing, so this one is blocked */
176
178
  get blocked() {
@@ -195,36 +197,25 @@ var PaintCanvasElement = class extends PcElement {
195
197
  this.app ??= new PaintCanvas();
196
198
  super.connectedCallback();
197
199
  this.resizeObserver?.observe(this);
198
- this.panelObserver.observe(this, {
199
- subtree: true,
200
- attributes: true,
201
- attributeFilter: [
202
- "icon",
203
- "label",
204
- "badge"
205
- ]
206
- });
207
200
  this.addEventListener("pointerdown", this.onPointerDown, true);
208
- this.addEventListener("keydown", this.onKeyDown);
201
+ this.addEventListener(PANE_COLLAPSE_EVENT, this.onPaneCollapse);
209
202
  document.addEventListener("pointerdown", this.onDocumentPointerDown, true);
210
203
  this.watchShortcuts();
211
204
  }
212
205
  disconnectedCallback() {
213
206
  super.disconnectedCallback();
214
207
  this.resizeObserver?.disconnect();
215
- this.panelObserver.disconnect();
216
208
  this.removeEventListener("pointerdown", this.onPointerDown, true);
217
- this.removeEventListener("keydown", this.onKeyDown);
209
+ this.removeEventListener(PANE_COLLAPSE_EVENT, this.onPaneCollapse);
218
210
  document.removeEventListener("pointerdown", this.onDocumentPointerDown, true);
219
211
  this.unwatchShortcuts();
220
212
  }
221
213
  willUpdate(changed) {
222
214
  super.willUpdate(changed);
223
215
  if (changed.has("app") || changed.has("store")) this.useStore(changed.get("app"));
216
+ if (changed.has("app") || changed.has("settingsStore")) this.useSettingsStore();
224
217
  this.watchAutosaveErrors();
225
218
  if (this.isConnected && (changed.has("app") || changed.has("shortcuts") || this.blocked !== this.blockedShortcuts)) this.watchShortcuts();
226
- const panel = this.openPanel;
227
- if (panel && this.modeOf(this.sideOf(panel)) !== "rail") this.closePanel(false);
228
219
  }
229
220
  updated() {
230
221
  const state = this.state;
@@ -238,11 +229,27 @@ var PaintCanvasElement = class extends PcElement {
238
229
  this.reported = report;
239
230
  this.dispatchEvent(new CustomEvent(STATE_EVENT, { detail: state }));
240
231
  }
232
+ this.setPaneModes();
241
233
  this.measureFloatingSides();
242
234
  }
243
- /** Keep the canvas clear of floating sides when fitting it */
235
+ /** The panes in a side: the default one, or those given for it */
236
+ panesIn(side) {
237
+ const slot = this.renderRoot.querySelector(`slot[name="${side}"]`);
238
+ const given = slot.assignedElements();
239
+ return (given.length ? given : [...slot.children]).filter((element) => element instanceof PcPane);
240
+ }
241
+ /** Show each side's panes in the side's mode, facing the canvas, with toolbars minimised while the layout is narrow */
242
+ setPaneModes() {
243
+ const narrow = this.width < this.narrowWidth;
244
+ for (const side of SIDES) for (const pane of this.panesIn(side)) {
245
+ pane.mode = this.modeOf(side);
246
+ pane.edge = side;
247
+ if (pane instanceof PcToolbar) pane.minimised = narrow;
248
+ }
249
+ }
250
+ /** Keep the canvas clear of floating and overlay sides when fitting it */
244
251
  measureFloatingSides() {
245
- const width = (side) => this.renderRoot.querySelector(`.side.${side} .float:not(.collapsed)`)?.getBoundingClientRect().width ?? 0;
252
+ const width = (side) => this.panesIn(side).filter((pane) => pane.mode === "floating" ? !pane.collapsed : pane.mode === "overlay").reduce((widest, pane) => Math.max(widest, pane.getBoundingClientRect().width), 0);
246
253
  const insets = {
247
254
  ...NO_INSETS,
248
255
  left: width("left"),
@@ -267,6 +274,20 @@ var PaintCanvasElement = class extends PcElement {
267
274
  }
268
275
  if (app.autosave?.store.name !== name) this.ownAutosave = app.useLayerStore(new IndexedDbLayerStore(name));
269
276
  }
277
+ /** Keeps the app settings under the `settings-store` name, if any (e.g. to wait until they are restored with `ready`) */
278
+ get settingsAutosave() {
279
+ return this.ownSettingsAutosave;
280
+ }
281
+ /**
282
+ * Keep the canvas's app settings under the `settings-store` name, stopping the previous settings autosave (which
283
+ * saves any change waiting to be saved).
284
+ */
285
+ useSettingsStore() {
286
+ this.ownSettingsAutosave?.stop();
287
+ const name = this.settingsStore || void 0;
288
+ this.ownSettingsAutosave = name === void 0 ? void 0 : new SettingsAutosave(this.app, new IndexedDbSettingsStore(name));
289
+ this.ownSettingsAutosave?.addEventListener(SETTINGS_AUTOSAVE_ERROR_EVENT, this.onSettingsAutosaveError);
290
+ }
270
291
  /** Show errors from the canvas's current autosave */
271
292
  watchAutosaveErrors() {
272
293
  const autosave = this.app?.autosave;
@@ -339,40 +360,6 @@ var PaintCanvasElement = class extends PcElement {
339
360
  }
340
361
  };
341
362
  }
342
- /** Which side a panel is in */
343
- sideOf(panel) {
344
- return panel.slot === "left" ? "left" : "right";
345
- }
346
- /**
347
- * Open a panel from its rail button (closing any other), or close it if it's open. Focus moves into the panel.
348
- */
349
- togglePanel(panel, opener) {
350
- if (this.openPanel === panel) {
351
- this.closePanel(true);
352
- return;
353
- }
354
- if (this.openPanel) this.closePanel(false);
355
- this.openPanel = panel;
356
- this.opener = opener;
357
- panel.toggleAttribute("data-pc-open", true);
358
- const focusable = panel.querySelector(FOCUSABLE);
359
- if (focusable) focusable.focus();
360
- else {
361
- panel.tabIndex = -1;
362
- panel.focus();
363
- }
364
- }
365
- /**
366
- * Close the panel open from a rail, moving focus back to its rail button if asked.
367
- */
368
- closePanel(returnFocus) {
369
- const panel = this.openPanel;
370
- if (!panel) return;
371
- panel.removeAttribute("data-pc-open");
372
- this.openPanel = void 0;
373
- if (returnFocus) this.opener?.focus();
374
- this.opener = void 0;
375
- }
376
363
  /** Follow which slots have content given (their defaults aren't needed) */
377
364
  onSlotChange(event) {
378
365
  const slot = event.target;
@@ -388,16 +375,12 @@ var PaintCanvasElement = class extends PcElement {
388
375
  >${this.filled[name] ? nothing : fallback}</slot
389
376
  >`;
390
377
  }
391
- /** Follow the panels given for a side, and whether it has a layers menu */
378
+ /** Follow whether a side given content has a layers menu */
392
379
  onSideSlotChange(side, event) {
393
380
  const assigned = event.target.assignedElements();
394
- this.panels = {
395
- ...this.panels,
396
- [side]: assigned.filter((element) => element.localName === "pc-panel")
397
- };
398
381
  this.sideLayers = {
399
382
  ...this.sideLayers,
400
- [side]: assigned.some((element) => element.matches("pc-layers-menu, :has(pc-layers-menu)"))
383
+ [side]: assigned.some((element) => element.matches("pc-panel-layers, :has(pc-panel-layers)"))
401
384
  };
402
385
  this.onSlotChange(event);
403
386
  }
@@ -408,99 +391,74 @@ var PaintCanvasElement = class extends PcElement {
408
391
  }
409
392
  /** Whether a side has a layers menu, replacing the toolbar's */
410
393
  get layersInSide() {
411
- return this.sideLayers.left || this.sideLayers.right;
412
- }
413
- /** The toolbar shown on the left unless the implementer gives other content */
414
- renderToolbar(app) {
415
- return html`<pc-toolbar
416
- .app=${app}
417
- .menu=${this.hasMenu}
418
- .layers=${!this.layersInSide}
419
- .menuText=${this.menuText}
420
- .menuLabel=${this.menuLabel}
421
- ></pc-toolbar>`;
422
- }
423
- /** A side's rail buttons, one per panel */
424
- renderRail(side, label) {
425
- const panels = this.panels[side];
426
- if (panels.length === 0) return nothing;
427
- return html`<div
428
- class="rail"
429
- part="${side}-rail"
430
- role="toolbar"
431
- aria-orientation="vertical"
432
- aria-label=${label}
433
- >
434
- ${panels.map((panel) => html`<button
435
- type="button"
436
- title=${panel.label}
437
- aria-label=${panel.badge ? this.t("paintCanvas.layout.new", { name: panel.label }) : panel.label}
438
- aria-expanded=${String(this.openPanel === panel)}
439
- @click=${(event) => this.togglePanel(panel, event.currentTarget)}
440
- >
441
- <img src=${this.iconUrl(panel.icon)} alt="" />
442
- ${panel.badge ? html`<span class="badge"></span>` : nothing}
443
- </button>`)}
444
- </div>`;
394
+ return this.sideLayers.left || this.sideLayers.right || !this.filled.right;
445
395
  }
446
- /** A side, in its mode */
396
+ /** A side's default content: the toolbar on the left, and the layers on the right */
397
+ renderSideDefault(side, app, label) {
398
+ if (side === "left") return html`<pc-toolbar
399
+ .app=${app}
400
+ .menu=${this.hasMenu}
401
+ .layers=${!this.layersInSide}
402
+ .menuText=${this.menuText}
403
+ .menuLabel=${this.menuLabel}
404
+ label=${label}
405
+ ></pc-toolbar>`;
406
+ return html`<pc-pane label=${label}
407
+ ><pc-panel-layers slot="start" .app=${app}></pc-panel-layers
408
+ ></pc-pane>`;
409
+ }
410
+ /** A side: its panes, in its mode (set on them after rendering) */
447
411
  renderSide(side, app, blocked) {
448
- const mode = this.modeOf(side);
449
412
  const label = this.t(side === "left" ? "paintCanvas.layout.tools" : "paintCanvas.layout.panels");
450
- const slot = html`<slot
451
- name=${side}
452
- @slotchange=${(event) => this.onSideSlotChange(side, event)}
453
- >${side === "left" && !this.filled.left ? this.renderToolbar(app) : nothing}</slot
454
- >`;
455
- let content = slot;
456
- if (mode === "rail") content = side === "left" ? html`${slot}${this.renderRail(side, label)}` : html`${this.renderRail(side, label)}${slot}`;
457
- else if (mode === "floating") {
458
- const collapsed = this.collapsed[side];
459
- const arrows = side === "left" ? ["»", "«"] : ["«", "»"];
460
- content = html`<div
461
- class=${collapsed ? "float collapsed" : "float"}
462
- part="${side}-float"
463
- >
464
- <button
465
- type="button"
466
- class="collapse"
467
- aria-expanded=${String(!collapsed)}
468
- aria-label=${this.t(collapsed ? "paintCanvas.layout.show" : "paintCanvas.layout.hide", { name: label })}
469
- @click=${() => this.collapsed = {
470
- ...this.collapsed,
471
- [side]: !collapsed
472
- }}
473
- >
474
- ${collapsed ? arrows[0] : arrows[1]}
475
- </button>
476
- <div class="content" ?hidden=${collapsed}>${slot}</div>
477
- </div>`;
478
- }
479
413
  return html`<div
480
414
  class="side ${side}"
481
415
  part=${side}
482
- data-mode=${mode}
416
+ data-mode=${this.modeOf(side)}
483
417
  role="region"
484
418
  aria-label=${label}
485
419
  ?inert=${blocked}
486
420
  >
487
- ${content}
421
+ <slot
422
+ name=${side}
423
+ @slotchange=${(event) => this.onSideSlotChange(side, event)}
424
+ >${this.filled[side] ? nothing : this.renderSideDefault(side, app, label)}</slot
425
+ >
488
426
  </div>`;
489
427
  }
428
+ /**
429
+ * Close the open panel (brush, eraser, layers, app menu or colour palette) on a press outside it and the button that
430
+ * opens it, marking the press (see `markDismissingPress`) so the canvas doesn't draw unless it's dragged. Presses in
431
+ * dialogs (e.g. one the panel opened) don't count as outside.
432
+ */
433
+ closePanelOutside(event) {
434
+ const panel = this.app?.panel;
435
+ if (!panel) return;
436
+ if (event.composedPath().some((target) => target instanceof Element && (target.classList.contains("panel") && target.getRootNode() === this.renderRoot || target.matches("dialog, pc-dialog") || target.getAttribute("data-panel") === panel || panel === Panel.Palette && target.localName === "pc-colour-pair"))) return;
437
+ markDismissingPress(event);
438
+ this.app.closePanel();
439
+ }
490
440
  /** The menus the toolbar opens, floating over the canvas beside it */
491
441
  renderPanels(app) {
492
- const brushMenu = (kind) => html`<pc-brush-menu
442
+ const brushMenu = (kind) => html`<pc-panel-brushes
493
443
  class="panel top-panel"
494
444
  kind=${kind}
495
445
  .app=${app}
496
446
  @pc-new-preset=${() => this.newPreset(kind)}
497
- ></pc-brush-menu>`;
447
+ ></pc-panel-brushes>`;
498
448
  return html`${app.panel === Panel.Brush ? brushMenu("brush") : nothing}
499
449
  ${app.panel === Panel.Eraser ? brushMenu("eraser") : nothing}
500
- ${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-layers-menu class="panel" .app=${app}></pc-layers-menu>` : nothing}
501
- <div class="panel menu-panel" ?open=${app.panel === Panel.Menu}>
450
+ ${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-panel-layers class="panel" .app=${app}></pc-panel-layers>` : nothing}
451
+ ${app.panel === Panel.Palette ? html`<pc-panel-colours
452
+ class="panel top-panel"
453
+ .app=${app}
454
+ ></pc-panel-colours>` : nothing}
455
+ <pc-panel
456
+ class="panel menu-panel"
457
+ label=${this.menuLabel || this.t("paintCanvas.toolbar.menu")}
458
+ ?open=${app.panel === Panel.Menu}
459
+ >
502
460
  <slot name="menu" @slotchange=${this.onMenuSlotChange}></slot>
503
- </div>`;
461
+ </pc-panel>`;
504
462
  }
505
463
  /** The brush configuration window, while open */
506
464
  renderConfig(app) {
@@ -549,14 +507,23 @@ var PaintCanvasElement = class extends PcElement {
549
507
  aria-label=${this.t("paintCanvas.layout.toolSettings")}
550
508
  ?inert=${blocked}
551
509
  >
552
- ${this.slotWithDefault("top", showsToolSettings ? html`<pc-tool-settings
553
- .app=${app}
554
- @pc-configure-brush=${(event) => this.configure(event.detail)}
555
- ></pc-tool-settings>` : nothing)}
510
+ ${this.slotWithDefault("top", showsToolSettings ? html`<pc-pane orientation="horizontal" edge="top"
511
+ ><pc-tool-settings
512
+ slot="middle"
513
+ .app=${app}
514
+ @pc-configure-brush=${(event) => this.configure(event.detail)}
515
+ ></pc-tool-settings
516
+ ><pc-clipboard-tools
517
+ slot="end"
518
+ .app=${app}
519
+ ?narrow=${this.width < this.narrowWidth}
520
+ ></pc-clipboard-tools
521
+ ></pc-pane>` : nothing)}
556
522
  </div>
557
523
  <div
558
524
  class="center"
559
525
  part="center"
526
+ style="--pc-left-inset: ${this.floatingInsets.left}px"
560
527
  role="region"
561
528
  aria-label=${this.t("paintCanvas.layout.canvas")}
562
529
  ?inert=${blocked}
@@ -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.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";
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/* Overlay: the side's rail sits over the canvas (beside it only, not the tool settings), so the canvas fills the\n width behind it; presses between its panes' buttons (which take presses themselves) reach the canvas */\n.side[data-mode=\"overlay\"] {\n grid-row: 3;\n grid-column: 1 / -1;\n pointer-events: none;\n}\n.side.left[data-mode=\"overlay\"] {\n justify-self: start;\n}\n.side.right[data-mode=\"overlay\"] {\n justify-self: end;\n}\n/* The toolbar's panels open beside a left overlay rail, not under it */\n:host([left-mode=\"overlay\"]) .panel {\n left: var(--pc-left-inset);\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, colours, theme and language). 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
+ }