@lvl99dotcom/paintcanvas 0.4.0 → 0.6.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 (224) hide show
  1. package/README.md +30 -15
  2. package/dist/lib/brushes/configRanges/index.d.ts +17 -0
  3. package/dist/lib/brushes/configRanges/index.js +31 -1
  4. package/dist/lib/brushes/params/index.d.ts +2 -1
  5. package/dist/lib/brushes/params/index.js +7 -4
  6. package/dist/lib/brushes/presets/round/index.d.ts +1 -1
  7. package/dist/lib/brushes/presets/round/index.js +12 -7
  8. package/dist/lib/brushes/presets/square/index.d.ts +1 -1
  9. package/dist/lib/brushes/presets/square/index.js +7 -3
  10. package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
  11. package/dist/lib/brushes/shapeImage/index.js +15 -9
  12. package/dist/lib/brushes/shapes/index.d.ts +18 -0
  13. package/dist/lib/brushes/shapes/index.js +86 -0
  14. package/dist/lib/brushes/types/index.d.ts +8 -0
  15. package/dist/lib/classes/BrushSettings/index.d.ts +29 -2
  16. package/dist/lib/classes/BrushSettings/index.js +51 -14
  17. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +31 -2
  18. package/dist/lib/classes/KeyboardShortcuts/index.js +66 -4
  19. package/dist/lib/classes/Layer/index.d.ts +10 -0
  20. package/dist/lib/classes/Layer/index.js +15 -1
  21. package/dist/lib/classes/LayerAutosave/index.d.ts +9 -15
  22. package/dist/lib/classes/LayerAutosave/index.js +30 -43
  23. package/dist/lib/classes/LayerStack/index.d.ts +15 -0
  24. package/dist/lib/classes/LayerStack/index.js +26 -2
  25. package/dist/lib/classes/PaintCanvas/index.d.ts +72 -3
  26. package/dist/lib/classes/PaintCanvas/index.js +106 -12
  27. package/dist/lib/classes/PngEncoder/index.d.ts +41 -0
  28. package/dist/lib/classes/PngEncoder/index.js +104 -0
  29. package/dist/lib/classes/ProfileGraphs/index.d.ts +87 -0
  30. package/dist/lib/classes/ProfileGraphs/index.js +148 -0
  31. package/dist/lib/classes/SettingsAutosave/index.d.ts +3 -2
  32. package/dist/lib/classes/SettingsAutosave/index.js +6 -3
  33. package/dist/lib/classes/UiSettings/index.d.ts +68 -0
  34. package/dist/lib/classes/UiSettings/index.js +82 -0
  35. package/dist/lib/messages/schema.d.ts +720 -0
  36. package/dist/lib/presets/schema.d.ts +360 -0
  37. package/dist/lib/presets/schema.js +8 -1
  38. package/dist/lib/profiles/index.d.ts +61 -0
  39. package/dist/lib/profiles/index.js +131 -0
  40. package/dist/lib/profiles/schema.d.ts +73 -0
  41. package/dist/lib/profiles/schema.js +53 -0
  42. package/dist/lib/rendering/sample/index.d.ts +11 -0
  43. package/dist/lib/rendering/sample/index.js +36 -5
  44. package/dist/lib/settings/index.d.ts +2 -1
  45. package/dist/lib/settings/index.js +7 -2
  46. package/dist/lib/settings/schema.d.ts +67 -0
  47. package/dist/lib/settings/schema.js +20 -0
  48. package/dist/lib/validation/validators.generated.js +13191 -3655
  49. package/dist/locales/en-GB.js +39 -2
  50. package/dist/locales/en-US.js +39 -2
  51. package/dist/locales/fr.js +39 -2
  52. package/dist/locales/ja.js +39 -2
  53. package/dist/locales/ko.js +39 -2
  54. package/dist/locales/zh-Hans.js +39 -2
  55. package/dist/locales/zh-Hant.js +39 -2
  56. package/dist/package.js +1 -1
  57. package/dist/testing/recordingCanvas.js +2 -1
  58. package/dist/ui/ask/index.d.ts +48 -0
  59. package/dist/ui/ask/index.js +67 -0
  60. package/dist/ui/base/index.d.ts +10 -3
  61. package/dist/ui/base/index.js +19 -4
  62. package/dist/ui/dismiss/index.d.ts +21 -0
  63. package/dist/ui/dismiss/index.js +27 -0
  64. package/dist/ui/drag/index.d.ts +51 -0
  65. package/dist/ui/drag/index.js +81 -0
  66. package/dist/ui/elements/PaintCanvas/index.d.ts +17 -12
  67. package/dist/ui/elements/PaintCanvas/index.js +36 -17
  68. package/dist/ui/elements/PaintCanvas/style.js +1 -1
  69. package/dist/ui/elements/PcAppSettings/index.d.ts +6 -6
  70. package/dist/ui/elements/PcAppSettings/index.js +21 -65
  71. package/dist/ui/elements/PcBrushConfig/index.d.ts +34 -16
  72. package/dist/ui/elements/PcBrushConfig/index.js +263 -153
  73. package/dist/ui/elements/PcBrushConfig/style.js +1 -1
  74. package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
  75. package/dist/ui/elements/PcBrushSize/index.js +66 -0
  76. package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
  77. package/dist/ui/elements/PcBrushTest/index.js +79 -6
  78. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  79. package/dist/ui/elements/PcButton/index.d.ts +12 -3
  80. package/dist/ui/elements/PcButton/index.js +18 -10
  81. package/dist/ui/elements/PcCanvas/index.d.ts +27 -2
  82. package/dist/ui/elements/PcCanvas/index.js +63 -13
  83. package/dist/ui/elements/PcCanvas/style.js +1 -1
  84. package/dist/ui/elements/PcCanvasMap/style.js +1 -1
  85. package/dist/ui/elements/PcClipboardTools/index.d.ts +49 -0
  86. package/dist/ui/elements/PcClipboardTools/index.js +125 -0
  87. package/dist/ui/elements/PcContextMenu/index.d.ts +2 -2
  88. package/dist/ui/elements/PcDialog/index.d.ts +46 -10
  89. package/dist/ui/elements/PcDialog/index.js +102 -51
  90. package/dist/ui/elements/PcDialogWindow/index.d.ts +141 -0
  91. package/dist/ui/elements/PcDialogWindow/index.js +385 -0
  92. package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
  93. package/dist/ui/elements/PcInputSettings/index.js +16 -1
  94. package/dist/ui/elements/PcMenu/index.d.ts +48 -3
  95. package/dist/ui/elements/PcMenu/index.js +111 -19
  96. package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
  97. package/dist/ui/elements/PcNumberSlider/index.js +247 -0
  98. package/dist/ui/elements/PcPane/index.d.ts +3 -1
  99. package/dist/ui/elements/PcPane/index.js +44 -7
  100. package/dist/ui/elements/PcPanel/index.d.ts +2 -2
  101. package/dist/ui/elements/PcPanel/index.js +8 -8
  102. package/dist/ui/elements/PcPanelBrushes/index.d.ts +5 -17
  103. package/dist/ui/elements/PcPanelBrushes/index.js +13 -48
  104. package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
  105. package/dist/ui/elements/PcPanelColours/index.js +2 -2
  106. package/dist/ui/elements/PcPanelColours/style.js +1 -1
  107. package/dist/ui/elements/PcPanelLayers/index.js +1 -1
  108. package/dist/ui/elements/PcPanelLayers/style.js +1 -1
  109. package/dist/ui/elements/PcPopover/index.d.ts +39 -1
  110. package/dist/ui/elements/PcPopover/index.js +72 -4
  111. package/dist/ui/elements/PcProfileEditor/index.d.ts +103 -0
  112. package/dist/ui/elements/PcProfileEditor/index.js +259 -0
  113. package/dist/ui/elements/PcProfileGraph/index.d.ts +84 -0
  114. package/dist/ui/elements/PcProfileGraph/index.js +317 -0
  115. package/dist/ui/elements/PcShortcutSettings/index.js +9 -13
  116. package/dist/ui/elements/PcSliderRange/index.d.ts +147 -0
  117. package/dist/ui/elements/PcSliderRange/index.js +307 -0
  118. package/dist/ui/elements/PcStats/index.d.ts +19 -4
  119. package/dist/ui/elements/PcStats/index.js +29 -13
  120. package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
  121. package/dist/ui/elements/PcThemePicker/index.js +22 -21
  122. package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
  123. package/dist/ui/elements/PcToolSettings/index.js +382 -318
  124. package/dist/ui/elements/PcToolSettings/style.js +1 -1
  125. package/dist/ui/elements/PcToolbar/index.d.ts +15 -0
  126. package/dist/ui/elements/PcToolbar/index.js +55 -14
  127. package/dist/ui/elements/PcToolbar/style.js +1 -1
  128. package/dist/ui/icons/images/icon-brush@2x.js +3 -0
  129. package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
  130. package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
  131. package/dist/ui/icons/images/icon-cog@2x.js +3 -0
  132. package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
  133. package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
  134. package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
  135. package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
  136. package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
  137. package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
  138. package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
  139. package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
  140. package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
  141. package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
  142. package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
  143. package/dist/ui/icons/images/icon-layers@2x.js +3 -0
  144. package/dist/ui/icons/images/icon-linked@2x.js +3 -0
  145. package/dist/ui/icons/images/icon-locked@2x.js +3 -0
  146. package/dist/ui/icons/images/icon-moon@2x.js +3 -0
  147. package/dist/ui/icons/images/icon-pan@2x.js +3 -0
  148. package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
  149. package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
  150. package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
  151. package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
  152. package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
  153. package/dist/ui/icons/images/icon-redo@2x.js +3 -0
  154. package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
  155. package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
  156. package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
  157. package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
  158. package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
  159. package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
  160. package/dist/ui/icons/images/icon-star@2x.js +3 -0
  161. package/dist/ui/icons/images/icon-sun@2x.js +3 -0
  162. package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
  163. package/dist/ui/icons/images/icon-undo@2x.js +3 -0
  164. package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
  165. package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
  166. package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
  167. package/dist/ui/icons/images/icon-visible@2x.js +3 -0
  168. package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
  169. package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
  170. package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
  171. package/dist/ui/icons/index.d.ts +1 -1
  172. package/dist/ui/icons/index.js +86 -72
  173. package/dist/ui/index.d.ts +13 -3
  174. package/dist/ui/index.js +15 -4
  175. package/dist/ui/layout/index.d.ts +9 -1
  176. package/dist/ui/layout/index.js +11 -2
  177. package/dist/ui/styles/index.d.ts +21 -1
  178. package/dist/ui/styles/index.js +73 -14
  179. package/dist/ui/tokens/index.d.ts +5 -4
  180. package/dist/ui/tokens/index.js +12 -7
  181. package/locales/en-GB.json +39 -2
  182. package/locales/en-US.json +39 -2
  183. package/locales/fr.json +39 -2
  184. package/locales/ja.json +39 -2
  185. package/locales/ko.json +39 -2
  186. package/locales/zh-Hans.json +39 -2
  187. package/locales/zh-Hant.json +39 -2
  188. package/package.json +27 -18
  189. package/dist/ui/icons/images/brush.js +0 -3
  190. package/dist/ui/icons/images/circle-mask.js +0 -3
  191. package/dist/ui/icons/images/clipboard.js +0 -3
  192. package/dist/ui/icons/images/cursor.js +0 -3
  193. package/dist/ui/icons/images/eraser.js +0 -3
  194. package/dist/ui/icons/images/eyedropper.js +0 -3
  195. package/dist/ui/icons/images/flip-horizontal.js +0 -3
  196. package/dist/ui/icons/images/flip-vertical.js +0 -3
  197. package/dist/ui/icons/images/focus-pen.js +0 -3
  198. package/dist/ui/icons/images/freehand-mask.js +0 -3
  199. package/dist/ui/icons/images/hidden.js +0 -3
  200. package/dist/ui/icons/images/layers.js +0 -3
  201. package/dist/ui/icons/images/linked.js +0 -3
  202. package/dist/ui/icons/images/locked.js +0 -3
  203. package/dist/ui/icons/images/moon.js +0 -3
  204. package/dist/ui/icons/images/pan.js +0 -3
  205. package/dist/ui/icons/images/polygon-mask.js +0 -3
  206. package/dist/ui/icons/images/pressure.js +0 -3
  207. package/dist/ui/icons/images/randomise.js +0 -3
  208. package/dist/ui/icons/images/rectangle-mask.js +0 -3
  209. package/dist/ui/icons/images/redo.js +0 -3
  210. package/dist/ui/icons/images/rotate-90-left.js +0 -3
  211. package/dist/ui/icons/images/rotate-90-right.js +0 -3
  212. package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
  213. package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
  214. package/dist/ui/icons/images/smoothing-linear.js +0 -3
  215. package/dist/ui/icons/images/star.js +0 -3
  216. package/dist/ui/icons/images/sun.js +0 -3
  217. package/dist/ui/icons/images/tilt.js +0 -3
  218. package/dist/ui/icons/images/undo.js +0 -3
  219. package/dist/ui/icons/images/unlinked.js +0 -3
  220. package/dist/ui/icons/images/unlocked.js +0 -3
  221. package/dist/ui/icons/images/visible.js +0 -3
  222. package/dist/ui/icons/images/zoom-in.js +0 -3
  223. package/dist/ui/icons/images/zoom-out.js +0 -3
  224. package/dist/ui/icons/images/zoom.js +0 -3
@@ -1,3 +1,3 @@
1
1
  // src/ui/elements/PcCanvas/style.css?inline
2
- var style_default = ":host {\n display: block;\n position: absolute;\n inset: 0;\n touch-action: none;\n}\ncanvas {\n display: block;\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n}\ncanvas.outlines,\ncanvas.sampler {\n pointer-events: none;\n}\ncanvas.overlay {\n pointer-events: none;\n /* Inverts what is underneath so the brush outline is visible on any colour */\n mix-blend-mode: difference;\n}\n";
2
+ var style_default = ":host {\n display: block;\n position: absolute;\n inset: 0;\n touch-action: none;\n}\ncanvas {\n display: block;\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n}\ncanvas[hidden] {\n display: none;\n}\ncanvas.outlines,\ncanvas.sampler {\n pointer-events: none;\n}\ncanvas.overlay {\n pointer-events: none;\n /* Inverts what is underneath so the brush outline is visible on any colour */\n mix-blend-mode: difference;\n}\n";
3
3
  export { style_default as default };
@@ -1,3 +1,3 @@
1
1
  // src/ui/elements/PcCanvasMap/style.css?inline
2
- var style_default = ":host {\n display: block;\n position: relative;\n touch-action: none;\n /* A solid grey border, drawn outside so the map and its frame keep their size and position */\n outline: 1px solid var(--pc-border);\n}\ncanvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n/* The part of the canvas the user can see */\n.frame {\n position: absolute;\n outline: 1px solid var(--pc-text);\n pointer-events: none;\n}\n";
2
+ var style_default = ":host {\n display: block;\n position: relative;\n touch-action: none;\n /* A solid grey border, drawn outside so the map and its frame keep their size and position */\n outline: 1px solid var(--pc-map-border);\n}\ncanvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n/* The part of the canvas the user can see */\n.frame {\n position: absolute;\n outline: 1px solid var(--pc-map-frame);\n pointer-events: none;\n}\n";
3
3
  export { style_default as default };
@@ -0,0 +1,49 @@
1
+ import { nothing } from "lit";
2
+ import { PcElement } from "../../base";
3
+ import PaintCanvas, { Tool } from "../../../lib/classes/PaintCanvas";
4
+ import "../PcContextMenu";
5
+ /** The tools that cut, copy and paste are shown for: the brush, eraser, lassos and Move tool */
6
+ export declare const CLIPBOARD_TOOLS: readonly Tool[];
7
+ /**
8
+ * Cut, copy, copy merged and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and
9
+ * Move tool (see `CLIPBOARD_TOOLS`). "Copy merged" copies the shown layers mixed as shown (see `PaintCanvas.copyMerged`).
10
+ * Cut and both copies work while there's a mask and no transform is pending; paste while there's copied image data.
11
+ * While `narrow`, they're one ⋮ menu (`pc-context-menu`) instead.
12
+ *
13
+ * `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
14
+ *
15
+ * @example
16
+ * html`<pc-clipboard-tools .app=${app} ?narrow=${narrow}></pc-clipboard-tools>`
17
+ */
18
+ export declare class PcClipboardTools extends PcElement {
19
+ static properties: {
20
+ app: {
21
+ attribute: boolean;
22
+ };
23
+ narrow: {
24
+ type: BooleanConstructor;
25
+ reflect: boolean;
26
+ };
27
+ translations: {
28
+ attribute: boolean;
29
+ };
30
+ icons: {
31
+ attribute: boolean;
32
+ };
33
+ theme: {
34
+ type: StringConstructor;
35
+ reflect: boolean;
36
+ };
37
+ };
38
+ static styles: import("lit").CSSResultGroup[];
39
+ app?: PaintCanvas;
40
+ /** Whether the buttons are one ⋮ menu (e.g. while the layout is narrow) */
41
+ narrow: boolean;
42
+ private changes;
43
+ constructor();
44
+ /** Cut, copy, copy merged and paste, each with whether it can be done now */
45
+ private actions;
46
+ /** Hide for tools without clipboard actions (e.g. zoom) */
47
+ protected willUpdate(): void;
48
+ render(): import("lit").TemplateResult<1> | typeof nothing | import("lit").TemplateResult<1>[];
49
+ }
@@ -0,0 +1,125 @@
1
+ import { Tool } from "../../../lib/classes/PaintCanvas/tool.js";
2
+ import { PcElement, defineElement } from "../../base/index.js";
3
+ import ChangeController from "../../ChangeController/index.js";
4
+ import { PILLS_CSS } from "../../styles/index.js";
5
+ import "../../../lib/classes/PaintCanvas/index.js";
6
+ import "../PcContextMenu/index.js";
7
+ import { css, html, nothing, unsafeCSS } from "lit";
8
+ // src/ui/elements/PcClipboardTools/index.ts
9
+ /** The tools that cut, copy and paste are shown for: the brush, eraser, lassos and Move tool */
10
+ var CLIPBOARD_TOOLS = [
11
+ Tool.Brush,
12
+ Tool.Eraser,
13
+ Tool.RectangleLasso,
14
+ Tool.CircleLasso,
15
+ Tool.PolygonLasso,
16
+ Tool.FreeformLasso,
17
+ Tool.Move
18
+ ];
19
+ /**
20
+ * Cut, copy, copy merged and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and
21
+ * Move tool (see `CLIPBOARD_TOOLS`). "Copy merged" copies the shown layers mixed as shown (see `PaintCanvas.copyMerged`).
22
+ * Cut and both copies work while there's a mask and no transform is pending; paste while there's copied image data.
23
+ * While `narrow`, they're one ⋮ menu (`pc-context-menu`) instead.
24
+ *
25
+ * `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
26
+ *
27
+ * @example
28
+ * html`<pc-clipboard-tools .app=${app} ?narrow=${narrow}></pc-clipboard-tools>`
29
+ */
30
+ var PcClipboardTools = class extends PcElement {
31
+ static properties = {
32
+ ...PcElement.properties,
33
+ app: { attribute: false },
34
+ narrow: {
35
+ type: Boolean,
36
+ reflect: true
37
+ }
38
+ };
39
+ static styles = [
40
+ PcElement.styles,
41
+ unsafeCSS(PILLS_CSS),
42
+ css`
43
+ :host {
44
+ display: flex;
45
+ flex: none;
46
+ align-items: center;
47
+ gap: 8px;
48
+ padding: 4px 8px;
49
+ background: var(--pc-bar);
50
+ border-bottom: 1px solid var(--pc-surface-active);
51
+ }
52
+ :host([hidden]) {
53
+ display: none;
54
+ }
55
+ `
56
+ ];
57
+ changes = new ChangeController(this, () => this.app ? [
58
+ this.app,
59
+ this.app.selection,
60
+ this.app.transform
61
+ ] : []);
62
+ constructor() {
63
+ super();
64
+ this.narrow = false;
65
+ }
66
+ /** Cut, copy, copy merged and paste, each with whether it can be done now */
67
+ actions(app) {
68
+ const noCopy = !app.selection.active || app.transform.pending;
69
+ return [
70
+ {
71
+ name: "cut",
72
+ label: this.t("paintCanvas.common.cut"),
73
+ disabled: noCopy,
74
+ run: () => app.cut()
75
+ },
76
+ {
77
+ name: "copy",
78
+ label: this.t("paintCanvas.common.copy"),
79
+ disabled: noCopy,
80
+ run: () => app.copy()
81
+ },
82
+ {
83
+ name: "copy-merged",
84
+ label: this.t("paintCanvas.common.copyMerged"),
85
+ disabled: noCopy,
86
+ run: () => app.copyMerged()
87
+ },
88
+ {
89
+ name: "paste",
90
+ label: this.t("paintCanvas.common.paste"),
91
+ disabled: !app.hasClipboard,
92
+ run: () => app.paste()
93
+ }
94
+ ];
95
+ }
96
+ /** Hide for tools without clipboard actions (e.g. zoom) */
97
+ willUpdate() {
98
+ this.hidden = !this.app || !CLIPBOARD_TOOLS.includes(this.app.tool);
99
+ }
100
+ render() {
101
+ const app = this.app;
102
+ if (!app || this.hidden) return nothing;
103
+ const actions = this.actions(app);
104
+ if (this.narrow) return html`<pc-context-menu
105
+ class="more"
106
+ label=${this.t("paintCanvas.toolSettings.moreActions")}
107
+ .items=${actions.map(({ name, label, disabled }) => ({
108
+ value: name,
109
+ label,
110
+ disabled
111
+ }))}
112
+ @pc-select=${(event) => actions.find((action) => action.name === event.detail.value)?.run()}
113
+ ></pc-context-menu>`;
114
+ return actions.map((action) => html`<button
115
+ type="button"
116
+ class="pill-s ${action.name}"
117
+ ?disabled=${action.disabled}
118
+ @click=${action.run}
119
+ >
120
+ ${action.label}
121
+ </button>`);
122
+ }
123
+ };
124
+ defineElement("pc-clipboard-tools", PcClipboardTools);
125
+ export { CLIPBOARD_TOOLS, PcClipboardTools };
@@ -1,6 +1,6 @@
1
1
  import { type PropertyValues } from "lit";
2
2
  import { PcElement } from "../../base";
3
- import { PcMenu, type MenuItem } from "../PcMenu";
3
+ import { PcMenu, type MenuEntry } from "../PcMenu";
4
4
  import "../PcMenu";
5
5
  /** Space kept between a context menu's menu and the window's edges */
6
6
  export declare const MENU_MARGIN = 8;
@@ -77,7 +77,7 @@ export declare class PcContextMenu extends PcElement {
77
77
  /** Whether the menu is open */
78
78
  open: boolean;
79
79
  /** The items of its own menu, when no `pc-menu` is slotted */
80
- items: readonly MenuItem[];
80
+ items: readonly MenuEntry[];
81
81
  /** Names the button for screen readers */
82
82
  label: string;
83
83
  /** Names the menu for screen readers (the button's `label` by default) */
@@ -1,6 +1,7 @@
1
1
  import { type PropertyValues } from "lit";
2
2
  import { PcElement } from "../../base";
3
3
  import "../PcButton";
4
+ import "../PcDialogWindow";
4
5
  import type { ButtonVariant } from "../PcButton";
5
6
  /** A button in a dialog */
6
7
  export interface DialogChoice {
@@ -12,14 +13,27 @@ export interface DialogChoice {
12
13
  /** The button's style instead (e.g. `dismiss`: pink text without a background) */
13
14
  variant?: ButtonVariant;
14
15
  }
15
- /** What `pc-choose` gives when the dialog is dismissed with Escape */
16
+ /** A text field in a dialog, e.g. for a name */
17
+ export interface DialogField {
18
+ label: string;
19
+ /** The text it starts with */
20
+ value: string;
21
+ }
22
+ /** What `pc-choose` gives: the chosen value, and the field's text when the dialog has a field */
23
+ export interface DialogChoiceDetail {
24
+ value: string;
25
+ text?: string;
26
+ }
27
+ /** What `pc-choose` gives when the dialog is dismissed (Escape, or a press outside it) */
16
28
  export declare const CANCEL_CHOICE = "cancel";
17
29
  /**
18
- * A modal window asking the user to choose, over a dimmed backdrop, with a heading, a message, any content of its own,
19
- * and its choices, each on its own row (the cancel choice and those marked `cancelling` in pink, the others purple,
20
- * unless a choice gives its own `variant`).
21
- * Focus moves to the first choice when it opens. Fires `pc-choose` with the chosen value (`cancel` for Escape, pressed
22
- * anywhere while it's open).
30
+ * A modal window asking the user to choose (a `<pc-dialog-window>` over a shaded page), with a heading, a message, an
31
+ * optional text field (`field`, e.g. for a name), any content of its own, and its choices, each on its own row (the
32
+ * cancel choice and those marked `cancelling` in pink, the others purple, unless a choice gives its own `variant`).
33
+ *
34
+ * Focus moves to the field, or else the first choice, when it opens. Fires `pc-choose` with the chosen value (and the
35
+ * field's `text`): Enter in the field chooses the first choice that isn't cancelling; Escape and a press outside it
36
+ * choose `cancel`, unless it's not `dismissible`. `inline` shows it over its nearest positioned ancestor only.
23
37
  *
24
38
  * @example
25
39
  * html`<pc-dialog heading="Delete layer" message='Delete "Sketch"?'
@@ -37,6 +51,16 @@ export declare class PcDialog extends PcElement {
37
51
  choices: {
38
52
  attribute: boolean;
39
53
  };
54
+ field: {
55
+ attribute: boolean;
56
+ };
57
+ dismissible: {
58
+ type: BooleanConstructor;
59
+ };
60
+ inline: {
61
+ type: BooleanConstructor;
62
+ reflect: boolean;
63
+ };
40
64
  translations: {
41
65
  attribute: boolean;
42
66
  };
@@ -52,12 +76,24 @@ export declare class PcDialog extends PcElement {
52
76
  heading: string;
53
77
  message: string;
54
78
  choices: readonly DialogChoice[];
79
+ /** A text field to fill in, if any */
80
+ field?: DialogField;
81
+ /** Whether Escape and a press outside it choose `cancel` */
82
+ dismissible: boolean;
83
+ /** Whether it covers only its nearest positioned ancestor, leaving the rest of the page usable */
84
+ inline: boolean;
55
85
  constructor();
56
86
  firstUpdated(changed: PropertyValues<this>): void;
57
- /** Choose one of the choices (or `cancel`) */
87
+ /**
88
+ * Move focus to the field (selecting its text), or else the first choice, once the window has opened (nothing in it
89
+ * can take focus before) and the choice has rendered its button.
90
+ */
91
+ private focusFirst;
92
+ /**
93
+ * Choose one of the choices (or `cancel`), giving the field's text when there's a field.
94
+ */
58
95
  choose(value: string): void;
59
- private readonly onKeyDown;
60
- connectedCallback(): void;
61
- disconnectedCallback(): void;
96
+ private readonly onFieldKeyDown;
97
+ private readonly onDismiss;
62
98
  render(): import("lit").TemplateResult<1>;
63
99
  }
@@ -1,15 +1,18 @@
1
1
  import { PcElement, defineElement, emit } from "../../base/index.js";
2
2
  import "../PcButton/index.js";
3
+ import "../PcDialogWindow/index.js";
3
4
  import { css, html, nothing } from "lit";
4
5
  // src/ui/elements/PcDialog/index.ts
5
- /** What `pc-choose` gives when the dialog is dismissed with Escape */
6
+ /** What `pc-choose` gives when the dialog is dismissed (Escape, or a press outside it) */
6
7
  var CANCEL_CHOICE = "cancel";
7
8
  /**
8
- * A modal window asking the user to choose, over a dimmed backdrop, with a heading, a message, any content of its own,
9
- * and its choices, each on its own row (the cancel choice and those marked `cancelling` in pink, the others purple,
10
- * unless a choice gives its own `variant`).
11
- * Focus moves to the first choice when it opens. Fires `pc-choose` with the chosen value (`cancel` for Escape, pressed
12
- * anywhere while it's open).
9
+ * A modal window asking the user to choose (a `<pc-dialog-window>` over a shaded page), with a heading, a message, an
10
+ * optional text field (`field`, e.g. for a name), any content of its own, and its choices, each on its own row (the
11
+ * cancel choice and those marked `cancelling` in pink, the others purple, unless a choice gives its own `variant`).
12
+ *
13
+ * Focus moves to the field, or else the first choice, when it opens. Fires `pc-choose` with the chosen value (and the
14
+ * field's `text`): Enter in the field chooses the first choice that isn't cancelling; Escape and a press outside it
15
+ * choose `cancel`, unless it's not `dismissible`. `inline` shows it over its nearest positioned ancestor only.
13
16
  *
14
17
  * @example
15
18
  * html`<pc-dialog heading="Delete layer" message='Delete "Sketch"?'
@@ -21,32 +24,20 @@ var PcDialog = class extends PcElement {
21
24
  ...PcElement.properties,
22
25
  heading: { type: String },
23
26
  message: { type: String },
24
- choices: { attribute: false }
27
+ choices: { attribute: false },
28
+ field: { attribute: false },
29
+ dismissible: { type: Boolean },
30
+ inline: {
31
+ type: Boolean,
32
+ reflect: true
33
+ }
25
34
  };
26
35
  static styles = [PcElement.styles, css`
27
36
  :host {
28
- position: fixed;
29
- z-index: 20;
30
- inset: 0;
31
- display: flex;
32
- align-items: center;
33
- justify-content: center;
34
- padding: var(--pc-space-3);
35
- background: var(--pc-backdrop);
37
+ display: contents;
36
38
  }
37
- [role="dialog"] {
38
- display: flex;
39
- width: 100%;
40
- max-width: 400px;
41
- flex-direction: column;
42
- gap: var(--pc-space-2);
43
- padding: var(--pc-space-3);
44
- border-radius: var(--pc-radius);
45
- background: var(--pc-surface-raised);
46
- color: var(--pc-text);
47
- box-shadow: var(--pc-shadow);
48
- font-family: var(--pc-font);
49
- font-size: var(--pc-font-size);
39
+ pc-dialog-window {
40
+ --pc-dialog-width: 400px;
50
41
  }
51
42
  h2 {
52
43
  margin: 0;
@@ -55,6 +46,30 @@ var PcDialog = class extends PcElement {
55
46
  p {
56
47
  margin: 0;
57
48
  }
49
+ .body {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: var(--pc-space-2);
53
+ }
54
+ label {
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: var(--pc-space-1);
58
+ }
59
+ input {
60
+ box-sizing: border-box;
61
+ width: 100%;
62
+ min-height: var(--pc-control-size);
63
+ padding: 0 var(--pc-space-2);
64
+ border: 1px solid var(--pc-border);
65
+ border-radius: var(--pc-radius-small);
66
+ background: var(--pc-surface);
67
+ color: var(--pc-text);
68
+ font: inherit;
69
+ }
70
+ input:focus-visible {
71
+ outline: var(--pc-focus-width) solid var(--pc-focus);
72
+ }
58
73
  .choices {
59
74
  display: flex;
60
75
  flex-direction: column;
@@ -66,41 +81,77 @@ var PcDialog = class extends PcElement {
66
81
  this.heading = "";
67
82
  this.message = "";
68
83
  this.choices = [];
84
+ this.dismissible = true;
85
+ this.inline = false;
69
86
  }
70
87
  firstUpdated(changed) {
71
88
  super.firstUpdated(changed);
89
+ this.focusFirst();
90
+ }
91
+ /**
92
+ * Move focus to the field (selecting its text), or else the first choice, once the window has opened (nothing in it
93
+ * can take focus before) and the choice has rendered its button.
94
+ */
95
+ async focusFirst() {
96
+ await this.renderRoot.querySelector("pc-dialog-window").updateComplete;
97
+ const field = this.renderRoot.querySelector("input");
98
+ if (field) {
99
+ field.focus();
100
+ field.select();
101
+ return;
102
+ }
72
103
  const first = this.renderRoot.querySelector("pc-button");
73
- if (first) first.updateComplete.then(() => first.focus());
104
+ if (!first) return;
105
+ await first.updateComplete;
106
+ first.focus();
74
107
  }
75
- /** Choose one of the choices (or `cancel`) */
108
+ /**
109
+ * Choose one of the choices (or `cancel`), giving the field's text when there's a field.
110
+ */
76
111
  choose(value) {
77
- emit(this, "pc-choose", { value });
112
+ const text = this.renderRoot.querySelector("input")?.value;
113
+ emit(this, "pc-choose", text === void 0 ? { value } : {
114
+ value,
115
+ text
116
+ });
78
117
  }
79
- onKeyDown = (event) => {
80
- if (event.key !== "Escape") return;
118
+ onFieldKeyDown = (event) => {
119
+ if (event.key !== "Enter") return;
120
+ const choice = this.choices.find((item) => item.value !== "cancel" && !item.cancelling);
121
+ if (!choice) return;
122
+ event.preventDefault();
123
+ this.choose(choice.value);
124
+ };
125
+ onDismiss = (event) => {
81
126
  event.preventDefault();
82
127
  this.choose(CANCEL_CHOICE);
83
128
  };
84
- connectedCallback() {
85
- super.connectedCallback();
86
- window.addEventListener("keydown", this.onKeyDown);
87
- }
88
- disconnectedCallback() {
89
- super.disconnectedCallback();
90
- window.removeEventListener("keydown", this.onKeyDown);
91
- }
92
129
  render() {
93
- return html`<div
130
+ const field = this.field;
131
+ return html`<pc-dialog-window
94
132
  part="dialog"
95
- role="dialog"
96
- aria-modal="true"
97
- aria-labelledby="heading"
98
- aria-describedby=${this.message ? "message" : nothing}
133
+ open
134
+ shaded
135
+ closable="false"
136
+ .dismissible=${this.dismissible}
137
+ ?inline=${this.inline}
138
+ description=${this.message || nothing}
139
+ @pc-dismiss=${this.onDismiss}
99
140
  >
100
- <h2 id="heading" part="heading">${this.heading}</h2>
101
- ${this.message ? html`<p id="message" part="message">${this.message}</p>` : nothing}
102
- <slot></slot>
103
- <div class="choices" part="choices">
141
+ <h2 slot="header-middle" part="heading">${this.heading}</h2>
142
+ <div slot="body" class="body">
143
+ ${this.message ? html`<p part="message">${this.message}</p>` : nothing}
144
+ ${field ? html`<label part="field">
145
+ ${field.label}
146
+ <input
147
+ type="text"
148
+ .value=${field.value}
149
+ @keydown=${this.onFieldKeyDown}
150
+ />
151
+ </label>` : nothing}
152
+ <slot></slot>
153
+ </div>
154
+ <div slot="footer-middle" class="choices" part="choices">
104
155
  ${this.choices.map((choice) => html`<pc-button
105
156
  variant=${choice.variant ?? (choice.value === "cancel" || choice.cancelling ? "cancel" : "accent")}
106
157
  data-value=${choice.value}
@@ -108,7 +159,7 @@ var PcDialog = class extends PcElement {
108
159
  >${choice.label}</pc-button
109
160
  >`)}
110
161
  </div>
111
- </div>`;
162
+ </pc-dialog-window>`;
112
163
  }
113
164
  };
114
165
  defineElement("pc-dialog", PcDialog);
@@ -0,0 +1,141 @@
1
+ import { type PropertyValues } from "lit";
2
+ import { PcElement } from "../../base";
3
+ /** Why the user asked to close a dialog window: its ⨉ button, the Escape key, or a press outside it */
4
+ export type DialogCloseReason = "close" | "escape" | "outside";
5
+ /** Event fired when the user asks to close a dialog window (detail: `{ reason }`); cancel it to keep the window open */
6
+ export declare const DIALOG_DISMISS_EVENT = "pc-dismiss";
7
+ /**
8
+ * A window over the page, built on the browser's `<dialog>`: centred in the viewport, never bigger than it, with a
9
+ * header, a body and a footer. While it's `open`, the rest of the page can't be used or scrolled and keyboard focus stays
10
+ * in it (moving to its first control, and back to where it was once it closes).
11
+ *
12
+ * Content goes in its slots: `header-start`, `header-middle` (which names the window for screen readers, unless it has a
13
+ * `label`; `description` describes it), `header-end`, `body` (or no slot), `footer-start`, `footer-middle` and `footer-end`. The body scrolls when
14
+ * there's too much to fit, keeping the header and footer in view. The header and footer only show with content (or the
15
+ * ⨉ button).
16
+ *
17
+ * Options:
18
+ * - `closable` (on by default; `closable="false"` turns it off): a ⨉ button at the top right.
19
+ * - `shaded`: shade the page under it.
20
+ * - `dismissible` (on by default; `dismissible="false"` turns it off): a press outside it closes it.
21
+ * - `inline`: cover only its nearest positioned ancestor instead of the whole page, which stays usable (and scrolls).
22
+ *
23
+ * The ⨉ button, a press outside it (while `dismissible`) and Escape (while `closable` or `dismissible`) fire a cancellable
24
+ * `pc-dismiss` event (detail: `{ reason }`, one of `close`, `outside` or `escape`); unless it's cancelled, the window
25
+ * closes (`open` becomes false). `--pc-dialog-width` sets its width (fitted to its content by default).
26
+ *
27
+ * @example
28
+ * html`<pc-dialog-window open shaded label="Keyboard shortcuts" @pc-dismiss=${close}>
29
+ * <h2 slot="header-middle">Keyboard shortcuts</h2>
30
+ * <pc-shortcut-settings slot="body" .settings=${settings}></pc-shortcut-settings>
31
+ * <pc-button slot="footer-end" @click=${close}>Done</pc-button>
32
+ * </pc-dialog-window>`
33
+ */
34
+ export declare class PcDialogWindow extends PcElement {
35
+ static properties: {
36
+ open: {
37
+ type: BooleanConstructor;
38
+ reflect: boolean;
39
+ };
40
+ closable: {
41
+ converter: {
42
+ fromAttribute: (value: string | null) => boolean;
43
+ toAttribute: (value: boolean) => "" | "false";
44
+ };
45
+ reflect: boolean;
46
+ };
47
+ shaded: {
48
+ type: BooleanConstructor;
49
+ reflect: boolean;
50
+ };
51
+ dismissible: {
52
+ converter: {
53
+ fromAttribute: (value: string | null) => boolean;
54
+ toAttribute: (value: boolean) => "" | "false";
55
+ };
56
+ reflect: boolean;
57
+ };
58
+ inline: {
59
+ type: BooleanConstructor;
60
+ reflect: boolean;
61
+ };
62
+ label: {
63
+ type: StringConstructor;
64
+ };
65
+ description: {
66
+ type: StringConstructor;
67
+ };
68
+ filled: {
69
+ state: boolean;
70
+ };
71
+ translations: {
72
+ attribute: boolean;
73
+ };
74
+ icons: {
75
+ attribute: boolean;
76
+ };
77
+ theme: {
78
+ type: StringConstructor;
79
+ reflect: boolean;
80
+ };
81
+ };
82
+ static styles: import("lit").CSSResultGroup[];
83
+ /** Whether it's shown */
84
+ open: boolean;
85
+ /** Whether it has a ⨉ button at the top right */
86
+ closable: boolean;
87
+ /** Whether the page under it is shaded */
88
+ shaded: boolean;
89
+ /** Whether a press outside it closes it */
90
+ dismissible: boolean;
91
+ /** Whether it covers only its nearest positioned ancestor, leaving the rest of the page usable */
92
+ inline: boolean;
93
+ /** Its name for screen readers, instead of its `header-middle` content */
94
+ label?: string;
95
+ /** A description for screen readers, e.g. the question a prompt asks */
96
+ description?: string;
97
+ /** The names of its header and footer slots that have content */
98
+ filled: ReadonlySet<string>;
99
+ /** Whether it's keeping the page from scrolling */
100
+ private scrollLocked;
101
+ /** What had focus before it opened, to give it back to if it's taken out of the page while open */
102
+ private returnFocus;
103
+ /** Whether the `<dialog>` was last shown inline (not modal) */
104
+ private shownInline;
105
+ /** Whether the press being made started outside the window */
106
+ private pressedOutside;
107
+ constructor();
108
+ /** The `<dialog>` it's built on */
109
+ private get dialog();
110
+ /**
111
+ * Ask to close the window, as the user would: fires `pc-dismiss` (detail: `{ reason }`), and closes it unless that's
112
+ * cancelled.
113
+ *
114
+ * @example
115
+ * dialogWindow.requestClose("close");
116
+ */
117
+ requestClose(reason: DialogCloseReason): void;
118
+ connectedCallback(): void;
119
+ disconnectedCallback(): void;
120
+ protected firstUpdated(changed: PropertyValues<this>): void;
121
+ protected updated(changed: PropertyValues<this>): void;
122
+ /**
123
+ * Show or close the `<dialog>` to match `open` (modal unless `inline`), keeping the page from scrolling while a modal
124
+ * window is open.
125
+ */
126
+ private syncOpen;
127
+ /** Keep the page from scrolling while a modal window is open in it, and let it scroll again otherwise */
128
+ private syncScrollLock;
129
+ /** Note which header and footer slots have content: only those (and the header and footer with them) are shown */
130
+ private updateRegions;
131
+ /** Whether any of a header's or footer's slots have content */
132
+ private hasRegion;
133
+ private readonly onKeyDown;
134
+ private readonly onCancel;
135
+ private readonly onClose;
136
+ private readonly onPointerDown;
137
+ private readonly onClick;
138
+ /** A header or footer's start, middle and end slots */
139
+ private renderRegion;
140
+ render(): import("lit").TemplateResult<1>;
141
+ }