@lvl99dotcom/paintcanvas 0.4.0 → 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 (174) hide show
  1. package/README.md +30 -15
  2. package/dist/lib/brushes/presets/round/index.d.ts +1 -1
  3. package/dist/lib/brushes/presets/round/index.js +12 -7
  4. package/dist/lib/brushes/presets/square/index.d.ts +1 -1
  5. package/dist/lib/brushes/presets/square/index.js +7 -3
  6. package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
  7. package/dist/lib/brushes/shapeImage/index.js +15 -9
  8. package/dist/lib/brushes/shapes/index.d.ts +18 -0
  9. package/dist/lib/brushes/shapes/index.js +86 -0
  10. package/dist/lib/classes/BrushSettings/index.d.ts +29 -2
  11. package/dist/lib/classes/BrushSettings/index.js +51 -14
  12. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +31 -2
  13. package/dist/lib/classes/KeyboardShortcuts/index.js +66 -4
  14. package/dist/lib/classes/PaintCanvas/index.d.ts +46 -0
  15. package/dist/lib/classes/PaintCanvas/index.js +67 -0
  16. package/dist/lib/classes/SettingsAutosave/index.d.ts +3 -2
  17. package/dist/lib/classes/SettingsAutosave/index.js +5 -3
  18. package/dist/lib/classes/UiSettings/index.d.ts +68 -0
  19. package/dist/lib/classes/UiSettings/index.js +82 -0
  20. package/dist/lib/settings/index.d.ts +1 -1
  21. package/dist/lib/settings/index.js +4 -2
  22. package/dist/lib/settings/schema.d.ts +18 -0
  23. package/dist/lib/settings/schema.js +14 -0
  24. package/dist/lib/validation/validators.generated.js +169 -1
  25. package/dist/locales/en-GB.js +9 -2
  26. package/dist/locales/en-US.js +9 -2
  27. package/dist/locales/fr.js +9 -2
  28. package/dist/locales/ja.js +9 -2
  29. package/dist/locales/ko.js +9 -2
  30. package/dist/locales/zh-Hans.js +9 -2
  31. package/dist/locales/zh-Hant.js +9 -2
  32. package/dist/package.js +1 -1
  33. package/dist/testing/recordingCanvas.js +2 -1
  34. package/dist/ui/base/index.d.ts +2 -2
  35. package/dist/ui/dismiss/index.d.ts +21 -0
  36. package/dist/ui/dismiss/index.js +27 -0
  37. package/dist/ui/drag/index.d.ts +51 -0
  38. package/dist/ui/drag/index.js +81 -0
  39. package/dist/ui/elements/PaintCanvas/index.d.ts +17 -12
  40. package/dist/ui/elements/PaintCanvas/index.js +34 -17
  41. package/dist/ui/elements/PaintCanvas/style.js +1 -1
  42. package/dist/ui/elements/PcAppSettings/index.d.ts +1 -1
  43. package/dist/ui/elements/PcAppSettings/index.js +1 -1
  44. package/dist/ui/elements/PcBrushConfig/index.d.ts +9 -2
  45. package/dist/ui/elements/PcBrushConfig/index.js +47 -7
  46. package/dist/ui/elements/PcBrushConfig/style.js +1 -1
  47. package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
  48. package/dist/ui/elements/PcBrushSize/index.js +66 -0
  49. package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
  50. package/dist/ui/elements/PcBrushTest/index.js +73 -5
  51. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  52. package/dist/ui/elements/PcCanvas/index.d.ts +21 -1
  53. package/dist/ui/elements/PcCanvas/index.js +53 -10
  54. package/dist/ui/elements/PcCanvasMap/style.js +1 -1
  55. package/dist/ui/elements/PcClipboardTools/index.d.ts +48 -0
  56. package/dist/ui/elements/PcClipboardTools/index.js +118 -0
  57. package/dist/ui/elements/PcContextMenu/index.d.ts +2 -2
  58. package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
  59. package/dist/ui/elements/PcInputSettings/index.js +16 -1
  60. package/dist/ui/elements/PcMenu/index.d.ts +48 -3
  61. package/dist/ui/elements/PcMenu/index.js +111 -19
  62. package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
  63. package/dist/ui/elements/PcNumberSlider/index.js +247 -0
  64. package/dist/ui/elements/PcPane/index.d.ts +3 -1
  65. package/dist/ui/elements/PcPane/index.js +44 -7
  66. package/dist/ui/elements/PcPanel/index.d.ts +2 -2
  67. package/dist/ui/elements/PcPanel/index.js +8 -8
  68. package/dist/ui/elements/PcPanelBrushes/index.d.ts +2 -15
  69. package/dist/ui/elements/PcPanelBrushes/index.js +6 -43
  70. package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
  71. package/dist/ui/elements/PcPopover/index.d.ts +39 -1
  72. package/dist/ui/elements/PcPopover/index.js +72 -4
  73. package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
  74. package/dist/ui/elements/PcThemePicker/index.js +22 -21
  75. package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
  76. package/dist/ui/elements/PcToolSettings/index.js +382 -318
  77. package/dist/ui/elements/PcToolSettings/style.js +1 -1
  78. package/dist/ui/elements/PcToolbar/index.d.ts +15 -0
  79. package/dist/ui/elements/PcToolbar/index.js +55 -14
  80. package/dist/ui/elements/PcToolbar/style.js +1 -1
  81. package/dist/ui/icons/images/icon-brush@2x.js +3 -0
  82. package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
  83. package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
  84. package/dist/ui/icons/images/icon-cog@2x.js +3 -0
  85. package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
  86. package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
  87. package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
  88. package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
  89. package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
  90. package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
  91. package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
  92. package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
  93. package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
  94. package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
  95. package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
  96. package/dist/ui/icons/images/icon-layers@2x.js +3 -0
  97. package/dist/ui/icons/images/icon-linked@2x.js +3 -0
  98. package/dist/ui/icons/images/icon-locked@2x.js +3 -0
  99. package/dist/ui/icons/images/icon-moon@2x.js +3 -0
  100. package/dist/ui/icons/images/icon-pan@2x.js +3 -0
  101. package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
  102. package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
  103. package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
  104. package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
  105. package/dist/ui/icons/images/icon-redo@2x.js +3 -0
  106. package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
  107. package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
  108. package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
  109. package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
  110. package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
  111. package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
  112. package/dist/ui/icons/images/icon-star@2x.js +3 -0
  113. package/dist/ui/icons/images/icon-sun@2x.js +3 -0
  114. package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
  115. package/dist/ui/icons/images/icon-undo@2x.js +3 -0
  116. package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
  117. package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
  118. package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
  119. package/dist/ui/icons/images/icon-visible@2x.js +3 -0
  120. package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
  121. package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
  122. package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
  123. package/dist/ui/icons/index.d.ts +1 -1
  124. package/dist/ui/icons/index.js +84 -72
  125. package/dist/ui/index.d.ts +7 -2
  126. package/dist/ui/index.js +10 -4
  127. package/dist/ui/layout/index.d.ts +9 -1
  128. package/dist/ui/layout/index.js +11 -2
  129. package/dist/ui/tokens/index.d.ts +5 -4
  130. package/dist/ui/tokens/index.js +12 -7
  131. package/locales/en-GB.json +9 -2
  132. package/locales/en-US.json +9 -2
  133. package/locales/fr.json +9 -2
  134. package/locales/ja.json +9 -2
  135. package/locales/ko.json +9 -2
  136. package/locales/zh-Hans.json +9 -2
  137. package/locales/zh-Hant.json +9 -2
  138. package/package.json +11 -4
  139. package/dist/ui/icons/images/brush.js +0 -3
  140. package/dist/ui/icons/images/circle-mask.js +0 -3
  141. package/dist/ui/icons/images/clipboard.js +0 -3
  142. package/dist/ui/icons/images/cursor.js +0 -3
  143. package/dist/ui/icons/images/eraser.js +0 -3
  144. package/dist/ui/icons/images/eyedropper.js +0 -3
  145. package/dist/ui/icons/images/flip-horizontal.js +0 -3
  146. package/dist/ui/icons/images/flip-vertical.js +0 -3
  147. package/dist/ui/icons/images/focus-pen.js +0 -3
  148. package/dist/ui/icons/images/freehand-mask.js +0 -3
  149. package/dist/ui/icons/images/hidden.js +0 -3
  150. package/dist/ui/icons/images/layers.js +0 -3
  151. package/dist/ui/icons/images/linked.js +0 -3
  152. package/dist/ui/icons/images/locked.js +0 -3
  153. package/dist/ui/icons/images/moon.js +0 -3
  154. package/dist/ui/icons/images/pan.js +0 -3
  155. package/dist/ui/icons/images/polygon-mask.js +0 -3
  156. package/dist/ui/icons/images/pressure.js +0 -3
  157. package/dist/ui/icons/images/randomise.js +0 -3
  158. package/dist/ui/icons/images/rectangle-mask.js +0 -3
  159. package/dist/ui/icons/images/redo.js +0 -3
  160. package/dist/ui/icons/images/rotate-90-left.js +0 -3
  161. package/dist/ui/icons/images/rotate-90-right.js +0 -3
  162. package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
  163. package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
  164. package/dist/ui/icons/images/smoothing-linear.js +0 -3
  165. package/dist/ui/icons/images/star.js +0 -3
  166. package/dist/ui/icons/images/sun.js +0 -3
  167. package/dist/ui/icons/images/tilt.js +0 -3
  168. package/dist/ui/icons/images/undo.js +0 -3
  169. package/dist/ui/icons/images/unlinked.js +0 -3
  170. package/dist/ui/icons/images/unlocked.js +0 -3
  171. package/dist/ui/icons/images/visible.js +0 -3
  172. package/dist/ui/icons/images/zoom-in.js +0 -3
  173. package/dist/ui/icons/images/zoom-out.js +0 -3
  174. package/dist/ui/icons/images/zoom.js +0 -3
@@ -0,0 +1,27 @@
1
+ // src/ui/dismiss/index.ts
2
+ /** Presses that closed a panel or menu by landing outside it */
3
+ var dismissingPresses = /* @__PURE__ */ new WeakSet();
4
+ /**
5
+ * Note that a press closed a panel or menu (by landing outside it), so what it landed on can treat it differently, e.g.
6
+ * the canvas only draws once it's dragged (see `isDismissingPress`). Call it from a capturing `pointerdown` listener,
7
+ * which runs before the listeners of what was pressed.
8
+ *
9
+ * @example
10
+ * document.addEventListener("pointerdown", (event) => {
11
+ * if (open && !event.composedPath().includes(panel)) {
12
+ * markDismissingPress(event);
13
+ * close();
14
+ * }
15
+ * }, true);
16
+ */
17
+ var markDismissingPress = (event) => {
18
+ dismissingPresses.add(event);
19
+ };
20
+ /**
21
+ * Whether a press closed a panel or menu (see `markDismissingPress`).
22
+ *
23
+ * @example
24
+ * if (isDismissingPress(event)) return; // just closing something
25
+ */
26
+ var isDismissingPress = (event) => dismissingPresses.has(event);
27
+ export { isDismissingPress, markDismissingPress };
@@ -0,0 +1,51 @@
1
+ /** How far (in pixels) a press must move before it counts as a drag, where a press can also be a tap */
2
+ export declare const DRAG_THRESHOLD = 4;
3
+ /** A pointer position in the viewport */
4
+ interface Point {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ /**
9
+ * How much a pointer moved between two points as one value: right and up are positive, left and down negative.
10
+ *
11
+ * @example
12
+ * dragDelta({ x: 0, y: 0 }, { x: 5, y: -5 }); // 10
13
+ */
14
+ export declare const dragDelta: (from: Point, to: Point) => number;
15
+ /**
16
+ * Turns pressing and dragging on an element into changes of a value: dragging right or up gives positive deltas, left
17
+ * or down negative ones (see `dragDelta`), in whole pixels (keeping any fraction for the next move). With a `threshold`, the press only becomes a drag once the pointer has moved
18
+ * that far from where it was pressed, so a press that barely moves can still be a tap (e.g. to focus a field).
19
+ *
20
+ * The element is given the pointer while dragging, so the drag carries on outside it.
21
+ *
22
+ * @example
23
+ * const drag = new PointerDrag((delta) => brush.resize(delta));
24
+ * html`<div @pointerdown=${drag.start} @pointermove=${drag.move} @pointerup=${drag.end} @pointercancel=${drag.end}></div>`
25
+ */
26
+ export declare class PointerDrag {
27
+ private readonly onDelta;
28
+ private readonly threshold;
29
+ /** Where the press started */
30
+ private origin;
31
+ /** The last position a delta was given from, once dragging */
32
+ private last;
33
+ /** Movement not yet given, under a whole pixel (pens can move by fractions of a pixel) */
34
+ private remainder;
35
+ constructor(onDelta: (delta: number) => void, threshold?: number);
36
+ /** Whether the press has become a drag */
37
+ get dragging(): boolean;
38
+ /**
39
+ * Start a press (on `pointerdown`).
40
+ */
41
+ readonly start: (event: PointerEvent) => void;
42
+ /**
43
+ * Give the change since the last move, once the press has become a drag (on `pointermove`).
44
+ */
45
+ readonly move: (event: PointerEvent) => void;
46
+ /**
47
+ * End the press (on `pointerup` or `pointercancel`). Returns whether it was a drag.
48
+ */
49
+ readonly end: () => boolean;
50
+ }
51
+ export {};
@@ -0,0 +1,81 @@
1
+ // src/ui/drag/index.ts
2
+ /** How far (in pixels) a press must move before it counts as a drag, where a press can also be a tap */
3
+ var DRAG_THRESHOLD = 4;
4
+ /**
5
+ * How much a pointer moved between two points as one value: right and up are positive, left and down negative.
6
+ *
7
+ * @example
8
+ * dragDelta({ x: 0, y: 0 }, { x: 5, y: -5 }); // 10
9
+ */
10
+ var dragDelta = (from, to) => to.x - from.x + (from.y - to.y);
11
+ /**
12
+ * Turns pressing and dragging on an element into changes of a value: dragging right or up gives positive deltas, left
13
+ * or down negative ones (see `dragDelta`), in whole pixels (keeping any fraction for the next move). With a `threshold`, the press only becomes a drag once the pointer has moved
14
+ * that far from where it was pressed, so a press that barely moves can still be a tap (e.g. to focus a field).
15
+ *
16
+ * The element is given the pointer while dragging, so the drag carries on outside it.
17
+ *
18
+ * @example
19
+ * const drag = new PointerDrag((delta) => brush.resize(delta));
20
+ * html`<div @pointerdown=${drag.start} @pointermove=${drag.move} @pointerup=${drag.end} @pointercancel=${drag.end}></div>`
21
+ */
22
+ var PointerDrag = class {
23
+ onDelta;
24
+ threshold;
25
+ /** Where the press started */
26
+ origin = null;
27
+ /** The last position a delta was given from, once dragging */
28
+ last = null;
29
+ /** Movement not yet given, under a whole pixel (pens can move by fractions of a pixel) */
30
+ remainder = 0;
31
+ constructor(onDelta, threshold = 0) {
32
+ this.onDelta = onDelta;
33
+ this.threshold = threshold;
34
+ }
35
+ /** Whether the press has become a drag */
36
+ get dragging() {
37
+ return this.last !== null;
38
+ }
39
+ /**
40
+ * Start a press (on `pointerdown`).
41
+ */
42
+ start = (event) => {
43
+ this.origin = {
44
+ x: event.clientX,
45
+ y: event.clientY
46
+ };
47
+ this.last = this.threshold > 0 ? null : this.origin;
48
+ this.remainder = 0;
49
+ event.currentTarget.setPointerCapture?.(event.pointerId);
50
+ };
51
+ /**
52
+ * Give the change since the last move, once the press has become a drag (on `pointermove`).
53
+ */
54
+ move = (event) => {
55
+ if (!this.origin) return;
56
+ const point = {
57
+ x: event.clientX,
58
+ y: event.clientY
59
+ };
60
+ if (!this.last) {
61
+ if (Math.hypot(point.x - this.origin.x, point.y - this.origin.y) < this.threshold) return;
62
+ this.last = this.origin;
63
+ event.preventDefault();
64
+ }
65
+ const total = this.remainder + dragDelta(this.last, point);
66
+ const delta = Math.trunc(total);
67
+ this.remainder = total - delta;
68
+ this.last = point;
69
+ if (delta !== 0) this.onDelta(delta);
70
+ };
71
+ /**
72
+ * End the press (on `pointerup` or `pointercancel`). Returns whether it was a drag.
73
+ */
74
+ end = () => {
75
+ const dragged = this.dragging;
76
+ this.origin = null;
77
+ this.last = null;
78
+ return dragged;
79
+ };
80
+ };
81
+ export { DRAG_THRESHOLD, PointerDrag, dragDelta };
@@ -5,8 +5,8 @@ import type { BrushConfig } from "../../../lib/brushes/types";
5
5
  import { type SaveMethod } from "../../../lib/rendering/screenshot";
6
6
  import { type InputKind, type LayoutState } from "../../layout";
7
7
  import "../PcPanel";
8
- import "../PcToolbar";
9
8
  import "../PcToolSettings";
9
+ import "../PcClipboardTools";
10
10
  import "../PcCanvas";
11
11
  import "../PcCanvasMap";
12
12
  import "../PcPanelBrushes";
@@ -20,8 +20,10 @@ import "../PcDialog";
20
20
  * window), with slots to add or replace content:
21
21
  *
22
22
  * - `header`: across the top (empty by default)
23
- * - `left`: `<pc-toolbar>` (a pane) by default
24
- * - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) by default
23
+ * - `left`: `<pc-toolbar>` (a pane) by default; toolbars in the sides are minimised (their tools one button that opens a
24
+ * menu of them) while the layout is narrow
25
+ * - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) and, at its end,
26
+ * `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow) by default
25
27
  * - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
26
28
  * - `center`: `<pc-canvas>` by default
27
29
  * - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
@@ -35,8 +37,9 @@ import "../PcDialog";
35
37
  *
36
38
  * Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
37
39
  * rail@600"`), which the layout gives the side's `<pc-pane>`s (see `PcPane`): `docked` beside the canvas; `rail`, a
38
- * column of buttons (one per panel) that open each panel over the canvas; or `floating`, the pane over the canvas as a
39
- * box that collapses to a button. Give side content in a `<pc-pane>` to have it follow the side's mode.
40
+ * column of buttons (one per panel) that open each panel over the canvas; `floating`, the pane over the canvas as a
41
+ * box that collapses to a button; or `overlay`, a rail over the canvas without a background (the canvas fills the
42
+ * width behind it). Give side content in a `<pc-pane>` to have it follow the side's mode.
40
43
  *
41
44
  * Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
42
45
  * `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
@@ -46,8 +49,8 @@ import "../PcDialog";
46
49
  * reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
47
50
  * that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
48
51
  *
49
- * With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette and
50
- * colours) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when they can't be.
52
+ * With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette, colours,
53
+ * theme and language) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when they can't be.
51
54
  *
52
55
  * @example
53
56
  * html`<paint-canvas style="height: 600px"></paint-canvas>`
@@ -163,7 +166,7 @@ export declare class PaintCanvasElement extends PcElement {
163
166
  private keys?;
164
167
  /** The state last reported, to report only changes */
165
168
  private reported?;
166
- /** Space the floating sides cover over the canvas */
169
+ /** Space the floating and overlay sides cover over the canvas */
167
170
  private floatingInsets;
168
171
  private readonly resizeObserver?;
169
172
  private readonly onPointerDown;
@@ -185,9 +188,9 @@ export declare class PaintCanvasElement extends PcElement {
185
188
  protected updated(): void;
186
189
  /** The panes in a side: the default one, or those given for it */
187
190
  private panesIn;
188
- /** Show each side's panes in the side's mode, facing the canvas */
191
+ /** Show each side's panes in the side's mode, facing the canvas, with toolbars minimised while the layout is narrow */
189
192
  private setPaneModes;
190
- /** Keep the canvas clear of floating sides when fitting it */
193
+ /** Keep the canvas clear of floating and overlay sides when fitting it */
191
194
  private measureFloatingSides;
192
195
  /**
193
196
  * Keep the canvas's layers under the `store` name, replacing an autosave the layout started earlier (also on the
@@ -238,9 +241,11 @@ export declare class PaintCanvasElement extends PcElement {
238
241
  /** A side: its panes, in its mode (set on them after rendering) */
239
242
  private renderSide;
240
243
  /**
241
- * Close the colour palette on a press outside it and the colours that open it.
244
+ * Close the open panel (brush, eraser, layers, app menu or colour palette) on a press outside it and the button that
245
+ * opens it, marking the press (see `markDismissingPress`) so the canvas doesn't draw unless it's dragged. Presses in
246
+ * dialogs (e.g. one the panel opened) don't count as outside.
242
247
  */
243
- private closePaletteOutside;
248
+ private closePanelOutside;
244
249
  /** The menus the toolbar opens, floating over the canvas beside it */
245
250
  private renderPanels;
246
251
  /** The brush configuration window, while open */
@@ -1,5 +1,6 @@
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";
@@ -13,7 +14,8 @@ import { DEFAULT_SIDE_MODES, STATE_EVENT, parseSideModes, sideModeFor } from "..
13
14
  import { hasToolSettings } from "../PcToolSettings/index.js";
14
15
  import "../PcPanel/index.js";
15
16
  import { PANE_COLLAPSE_EVENT, PcPane } from "../PcPane/index.js";
16
- import "../PcToolbar/index.js";
17
+ import { PcToolbar } from "../PcToolbar/index.js";
18
+ import "../PcClipboardTools/index.js";
17
19
  import "../PcCanvas/index.js";
18
20
  import "../PcCanvasMap/index.js";
19
21
  import "../PcPanelBrushes/index.js";
@@ -40,8 +42,10 @@ var TAKE_OVER_CHOICE = "take-over";
40
42
  * window), with slots to add or replace content:
41
43
  *
42
44
  * - `header`: across the top (empty by default)
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
+ * - `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
45
49
  * - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
46
50
  * - `center`: `<pc-canvas>` by default
47
51
  * - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
@@ -55,8 +59,9 @@ var TAKE_OVER_CHOICE = "take-over";
55
59
  *
56
60
  * Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
57
61
  * 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.
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.
60
65
  *
61
66
  * Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
62
67
  * `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
@@ -66,8 +71,8 @@ var TAKE_OVER_CHOICE = "take-over";
66
71
  * reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
67
72
  * that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
68
73
  *
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.
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.
71
76
  *
72
77
  * @example
73
78
  * html`<paint-canvas style="height: 600px"></paint-canvas>`
@@ -137,7 +142,7 @@ var PaintCanvasElement = class extends PcElement {
137
142
  keys;
138
143
  /** The state last reported, to report only changes */
139
144
  reported;
140
- /** Space the floating sides cover over the canvas */
145
+ /** Space the floating and overlay sides cover over the canvas */
141
146
  floatingInsets = NO_INSETS;
142
147
  resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(([entry]) => {
143
148
  this.width = Math.round(entry?.contentRect.width ?? 0);
@@ -157,7 +162,7 @@ var PaintCanvasElement = class extends PcElement {
157
162
  onShortcut = (event) => {
158
163
  if (event.detail.type === "save") this.saveScreenshot();
159
164
  };
160
- onDocumentPointerDown = (event) => this.closePaletteOutside(event.composedPath());
165
+ onDocumentPointerDown = (event) => this.closePanelOutside(event);
161
166
  onPaneCollapse = () => this.requestUpdate();
162
167
  constructor() {
163
168
  super();
@@ -233,16 +238,18 @@ var PaintCanvasElement = class extends PcElement {
233
238
  const given = slot.assignedElements();
234
239
  return (given.length ? given : [...slot.children]).filter((element) => element instanceof PcPane);
235
240
  }
236
- /** Show each side's panes in the side's mode, facing the canvas */
241
+ /** Show each side's panes in the side's mode, facing the canvas, with toolbars minimised while the layout is narrow */
237
242
  setPaneModes() {
243
+ const narrow = this.width < this.narrowWidth;
238
244
  for (const side of SIDES) for (const pane of this.panesIn(side)) {
239
245
  pane.mode = this.modeOf(side);
240
246
  pane.edge = side;
247
+ if (pane instanceof PcToolbar) pane.minimised = narrow;
241
248
  }
242
249
  }
243
- /** Keep the canvas clear of floating sides when fitting it */
250
+ /** Keep the canvas clear of floating and overlay sides when fitting it */
244
251
  measureFloatingSides() {
245
- const width = (side) => this.panesIn(side).filter((pane) => pane.mode === "floating" && !pane.collapsed).reduce((widest, pane) => Math.max(widest, pane.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"),
@@ -419,12 +426,16 @@ var PaintCanvasElement = class extends PcElement {
419
426
  </div>`;
420
427
  }
421
428
  /**
422
- * Close the colour palette on a press outside it and the colours that open it.
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.
423
432
  */
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();
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();
428
439
  }
429
440
  /** The menus the toolbar opens, floating over the canvas beside it */
430
441
  renderPanels(app) {
@@ -502,11 +513,17 @@ var PaintCanvasElement = class extends PcElement {
502
513
  .app=${app}
503
514
  @pc-configure-brush=${(event) => this.configure(event.detail)}
504
515
  ></pc-tool-settings
516
+ ><pc-clipboard-tools
517
+ slot="end"
518
+ .app=${app}
519
+ ?narrow=${this.width < this.narrowWidth}
520
+ ></pc-clipboard-tools
505
521
  ></pc-pane>` : nothing)}
506
522
  </div>
507
523
  <div
508
524
  class="center"
509
525
  part="center"
526
+ style="--pc-left-inset: ${this.floatingInsets.left}px"
510
527
  role="region"
511
528
  aria-label=${this.t("paintCanvas.layout.canvas")}
512
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/* 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";
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 };
@@ -6,7 +6,7 @@ import { type SaveMethod } from "../../../lib/rendering/screenshot";
6
6
  /**
7
7
  * Buttons for the user's app settings: "Keyboard shortcuts", which opens a window to change them (see
8
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
9
+ * settings, keyboard shortcuts, colour palette, colours, theme and language). The window is a modal `<dialog>` above the whole page; it
10
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
11
  * exporting fails.
12
12
  *
@@ -14,7 +14,7 @@ var SETTINGS_FILE_ERROR_KEYS = {
14
14
  /**
15
15
  * Buttons for the user's app settings: "Keyboard shortcuts", which opens a window to change them (see
16
16
  * `pc-shortcut-settings`), and "Import settings" and "Export settings", which read and write a settings JSON file (input
17
- * settings, keyboard shortcuts, colour palette and colours). The window is a modal `<dialog>` above the whole page; it
17
+ * settings, keyboard shortcuts, colour palette, colours, theme and language). The window is a modal `<dialog>` above the whole page; it
18
18
  * 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
19
19
  * exporting fails.
20
20
  *
@@ -23,7 +23,8 @@ export declare const SMOOTHING_CHOICES: readonly [{
23
23
  readonly icon: "smoothingBezierCubic";
24
24
  }];
25
25
  /**
26
- * The brush (or eraser) configuration window: a testing area, the shape (upload or paste), smoothing, width, height,
26
+ * The brush (or eraser) configuration window: a testing area, the shape (pressing it picks a built-in shape from the
27
+ * device's own list, which says "Custom" for an uploaded or pasted one; or upload or paste one), smoothing, width, height,
27
28
  * opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
28
29
  * pressure, tilt and randomness; when any is on, the setting has a min and max.
29
30
  *
@@ -82,7 +83,9 @@ export declare class PcBrushConfig extends PcElement {
82
83
  prompt: PromptFn;
83
84
  /** Turns files into brush shapes (replaceable for tests) */
84
85
  readShape: (file: File) => Promise<ShapeImage>;
85
- /** Reads a brush shape from the clipboard (replaceable for tests) */
86
+ /**
87
+ * Reads a brush shape from the clipboard, or else from image data copied on the canvas (replaceable for tests)
88
+ */
86
89
  readClipboard: () => Promise<ShapeImage>;
87
90
  private readonly onKeyDown;
88
91
  constructor();
@@ -124,6 +127,10 @@ export declare class PcBrushConfig extends PcElement {
124
127
  private slider;
125
128
  /** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
126
129
  private renderParam;
130
+ /**
131
+ * Use a built-in shape (see `BUILT_IN_SHAPES`), keeping the width; they're all square, so the height is the same.
132
+ */
133
+ private useBuiltInShape;
127
134
  /** Render the shape and smoothing row */
128
135
  private renderShape;
129
136
  render(): import("lit").TemplateResult<1> | typeof nothing;
@@ -6,8 +6,10 @@ import "../PcBrushTest/index.js";
6
6
  import { SmoothingType } from "../../../lib/brushes/types/index.js";
7
7
  import { PARAM_LIMITS, VARIATION_SOURCES, readRange, toggleSource, writeRange } from "../../../lib/brushes/configRanges/index.js";
8
8
  import { SHAPE_ERRORS, ShapeImageError, readClipboardShape, readShapeImage } from "../../../lib/brushes/shapeImage/index.js";
9
+ import { BUILT_IN_SHAPES, builtInShapeId } from "../../../lib/brushes/shapes/index.js";
9
10
  import "../../../lib/brushes/StrokeSampler/index.js";
10
11
  import { html, nothing, unsafeCSS } from "lit";
12
+ import { live } from "lit/directives/live.js";
11
13
  import { styleMap } from "lit/directives/style-map.js";
12
14
  // src/ui/elements/PcBrushConfig/index.ts
13
15
  /** The smoothing choices, in the order shown: value, translation key of the name, and icon name */
@@ -40,6 +42,8 @@ var SOURCE_LABELS = {
40
42
  tilt: "paintCanvas.toolSettings.tilt",
41
43
  randomise: "paintCanvas.brushConfig.randomise"
42
44
  };
45
+ /** The shape picker's value for a shape the user uploaded or pasted */
46
+ var CUSTOM_SHAPE = "custom";
43
47
  /** Translation keys of the brush shape error messages */
44
48
  var SHAPE_ERROR_KEYS = {
45
49
  [SHAPE_ERRORS.type]: "paintCanvas.shape.notPng",
@@ -55,7 +59,8 @@ var numberValue = (event) => {
55
59
  return Number.isFinite(value) ? value : void 0;
56
60
  };
57
61
  /**
58
- * The brush (or eraser) configuration window: a testing area, the shape (upload or paste), smoothing, width, height,
62
+ * The brush (or eraser) configuration window: a testing area, the shape (pressing it picks a built-in shape from the
63
+ * device's own list, which says "Custom" for an uploaded or pasted one; or upload or paste one), smoothing, width, height,
59
64
  * opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
60
65
  * pressure, tilt and randomness; when any is on, the setting has a min and max.
61
66
  *
@@ -88,8 +93,10 @@ var PcBrushConfig = class extends PcElement {
88
93
  prompt = (message, value) => window.prompt(message, value);
89
94
  /** Turns files into brush shapes (replaceable for tests) */
90
95
  readShape = readShapeImage;
91
- /** Reads a brush shape from the clipboard (replaceable for tests) */
92
- readClipboard = () => readClipboardShape();
96
+ /**
97
+ * Reads a brush shape from the clipboard, or else from image data copied on the canvas (replaceable for tests)
98
+ */
99
+ readClipboard = () => readClipboardShape(navigator.clipboard, {}, async () => this.app?.copiedPng());
93
100
  onKeyDown = (event) => {
94
101
  if (event.key === "Escape") this.close();
95
102
  };
@@ -295,15 +302,48 @@ var PcBrushConfig = class extends PcElement {
295
302
  </label>`)}
296
303
  </div>`;
297
304
  }
305
+ /**
306
+ * Use a built-in shape (see `BUILT_IN_SHAPES`), keeping the width; they're all square, so the height is the same.
307
+ */
308
+ useBuiltInShape(id) {
309
+ const shape = BUILT_IN_SHAPES.find((built) => built.id === id);
310
+ if (!shape) return;
311
+ const width = this.draft?.width ?? 1;
312
+ this.change({
313
+ image: shape.image,
314
+ height: width
315
+ });
316
+ }
298
317
  /** Render the shape and smoothing row */
299
318
  renderShape(draft) {
319
+ const shapeId = builtInShapeId(draft.image);
300
320
  const smoothing = draft.smoothingType === SmoothingType.None || draft.smoothingType === void 0 ? SmoothingType.Linear : draft.smoothingType;
301
321
  return html`<div class="row shape-row">
302
322
  <span class="label">${this.t("paintCanvas.brushConfig.shape")}</span>
303
- <span
304
- class=${draft.image ? "shape-preview image" : "shape-preview"}
305
- style=${styleMap({ maskImage: typeof draft.image === "string" ? `url("${draft.image}")` : void 0 })}
306
- ></span>
323
+ <span class="shape-picker">
324
+ <span
325
+ class=${draft.image ? "shape-preview image" : "shape-preview"}
326
+ style=${styleMap({ maskImage: typeof draft.image === "string" ? `url("${draft.image}")` : void 0 })}
327
+ ></span>
328
+ <!-- Over the shape, so pressing it opens the device's own list -->
329
+ <select
330
+ class="shape-select"
331
+ aria-label=${this.t("paintCanvas.brushConfig.shape")}
332
+ title=${this.t("paintCanvas.brushConfig.chooseShape")}
333
+ ?disabled=${this.busy}
334
+ @change=${(e) => this.useBuiltInShape(e.target.value)}
335
+ >
336
+ ${shapeId === void 0 ? html`<option value=${CUSTOM_SHAPE} .selected=${true}>
337
+ ${this.t("paintCanvas.brushConfig.customShape")}
338
+ </option>` : nothing}
339
+ ${BUILT_IN_SHAPES.map((shape) => html`<option
340
+ value=${shape.id}
341
+ .selected=${live(shape.id === shapeId)}
342
+ >
343
+ ${shape.name}
344
+ </option>`)}
345
+ </select>
346
+ </span>
307
347
  <button
308
348
  type="button"
309
349
  class="pill upload"
@@ -1,3 +1,3 @@
1
1
  // src/ui/elements/PcBrushConfig/style.css?inline
2
- var style_default = ":host {\n position: fixed;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--pc-backdrop);\n}\n[role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: min(560px, 95vw);\n max-height: 95vh;\n overflow: auto;\n padding: 8px;\n background: var(--pc-surface);\n}\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n.row > .label {\n width: 6em;\n}\ninput[type=\"number\"] {\n width: 4.5em;\n}\ninput[type=\"range\"] {\n flex: 1;\n min-width: 60px;\n}\n.shape-preview {\n display: inline-block;\n width: 40px;\n height: 40px;\n background: var(--pc-text);\n border-radius: 50%;\n}\n.shape-preview.image {\n border-radius: 0;\n mask-repeat: no-repeat;\n mask-size: contain;\n mask-position: center;\n}\n/* The name, with \"Close\" on the right */\n.header .close {\n margin-left: auto;\n}\n/* The shape (preview, Upload and Paste) and the smoothing icons on one row */\n.shape-row {\n flex-wrap: nowrap;\n}\n.shape-row .shape-preview {\n flex: none;\n}\nfieldset.smoothing {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 0 auto;\n padding: 0;\n border: 0;\n}\nfieldset.smoothing legend {\n float: left;\n margin-right: 4px;\n padding: 0;\n}\n";
2
+ var style_default = ":host {\n position: fixed;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--pc-backdrop);\n}\n[role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: min(560px, 95vw);\n max-height: 95vh;\n overflow: auto;\n padding: 8px;\n background: var(--pc-surface);\n}\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n.row > .label {\n width: 6em;\n}\ninput[type=\"number\"] {\n width: 4.5em;\n}\ninput[type=\"range\"] {\n flex: 1;\n min-width: 60px;\n}\n.shape-preview {\n display: inline-block;\n width: 40px;\n height: 40px;\n background: var(--pc-text);\n border-radius: 50%;\n}\n.shape-preview.image {\n border-radius: 0;\n mask-repeat: no-repeat;\n mask-size: contain;\n mask-position: center;\n}\n/* The name, with \"Close\" on the right */\n.header .close {\n margin-left: auto;\n}\n/* The shape (preview, Upload and Paste) and the smoothing icons on one row */\n.shape-row {\n flex-wrap: nowrap;\n}\n/* The shape, with an invisible list over it: pressing the shape opens the device's own list of shapes */\n.shape-picker {\n position: relative;\n display: inline-flex;\n flex: none;\n border-radius: var(--pc-radius-small);\n}\n.shape-picker:hover {\n background: var(--pc-surface-hover);\n}\n.shape-picker:focus-within {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.shape-select {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\nfieldset.smoothing {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 0 auto;\n padding: 0;\n border: 0;\n}\nfieldset.smoothing legend {\n float: left;\n margin-right: 4px;\n padding: 0;\n}\n";
3
3
  export { style_default as default };
@@ -0,0 +1,53 @@
1
+ import { nothing } from "lit";
2
+ import { PcElement } from "../../base";
3
+ import "../PcNumberSlider";
4
+ import { type default as BrushSettings } from "../../../lib/classes/BrushSettings";
5
+ /**
6
+ * A brush's size as text: one number when the width and height are the same, otherwise "width × height".
7
+ *
8
+ * @example
9
+ * sizeText({ width: 20, height: 20 }); // "20"
10
+ * sizeText({ width: 30, height: 10 }); // "30 × 10"
11
+ */
12
+ export declare const sizeText: ({ width, height, }: {
13
+ width: number;
14
+ height: number;
15
+ }) => string;
16
+ /**
17
+ * A brush or eraser's size, as a `pc-number-slider`: pressed and dragged right or up it gets bigger, left or down
18
+ * smaller, keeping its proportions (see `BrushSettings.resize`). Tapping it lets you type the larger side's size. With
19
+ * keyboard focus, the arrow keys change it by a pixel and Page Up and Page Down by a step (like `]` and `[`).
20
+ *
21
+ * @example
22
+ * html`<pc-brush-size .settings=${app.brush} label="Brush size"></pc-brush-size>`
23
+ */
24
+ export declare class PcBrushSize extends PcElement {
25
+ static properties: {
26
+ settings: {
27
+ attribute: boolean;
28
+ };
29
+ label: {
30
+ type: StringConstructor;
31
+ };
32
+ translations: {
33
+ attribute: boolean;
34
+ };
35
+ icons: {
36
+ attribute: boolean;
37
+ };
38
+ theme: {
39
+ type: StringConstructor;
40
+ reflect: boolean;
41
+ };
42
+ };
43
+ static styles: import("lit").CSSResultGroup[];
44
+ /** The brush or eraser settings it shows and changes */
45
+ settings?: BrushSettings;
46
+ /** Its name for screen readers, e.g. "Brush size" */
47
+ label: string;
48
+ private changes;
49
+ constructor();
50
+ /** Resize to the slider's value (the larger side), keeping the proportions */
51
+ private onChange;
52
+ render(): import("lit").TemplateResult<1> | typeof nothing;
53
+ }