@lvl99dotcom/paintcanvas 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +131 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/lib/brushes/BrushTipCache/index.d.ts +32 -0
- package/dist/lib/brushes/BrushTipCache/index.js +60 -0
- package/dist/lib/brushes/StrokeEngine/index.d.ts +171 -0
- package/dist/lib/brushes/StrokeEngine/index.js +324 -0
- package/dist/lib/brushes/StrokePath/index.d.ts +53 -0
- package/dist/lib/brushes/StrokePath/index.js +161 -0
- package/dist/lib/brushes/StrokeSampler/index.d.ts +55 -0
- package/dist/lib/brushes/StrokeSampler/index.js +75 -0
- package/dist/lib/brushes/authors/index.d.ts +55 -0
- package/dist/lib/brushes/authors/index.js +60 -0
- package/dist/lib/brushes/configRanges/index.d.ts +52 -0
- package/dist/lib/brushes/configRanges/index.js +206 -0
- package/dist/lib/brushes/index.d.ts +5 -0
- package/dist/lib/brushes/index.js +19 -0
- package/dist/lib/brushes/params/index.d.ts +108 -0
- package/dist/lib/brushes/params/index.js +139 -0
- package/dist/lib/brushes/presetFile/index.d.ts +32 -0
- package/dist/lib/brushes/presetFile/index.js +55 -0
- package/dist/lib/brushes/presets/drawing/brush-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/felt-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/index.d.ts +13 -0
- package/dist/lib/brushes/presets/drawing/index.js +88 -0
- package/dist/lib/brushes/presets/drawing/pencil-2b.js +3 -0
- package/dist/lib/brushes/presets/drawing/pencil-hb.js +3 -0
- package/dist/lib/brushes/presets/painting/grass.js +3 -0
- package/dist/lib/brushes/presets/painting/hair.js +3 -0
- package/dist/lib/brushes/presets/painting/index.d.ts +18 -0
- package/dist/lib/brushes/presets/painting/index.js +118 -0
- package/dist/lib/brushes/presets/painting/splatter.js +3 -0
- package/dist/lib/brushes/presets/painting/textured-paint.js +3 -0
- package/dist/lib/brushes/presets/painting/watery-paint.js +3 -0
- package/dist/lib/brushes/presets/round/index.d.ts +9 -0
- package/dist/lib/brushes/presets/round/index.js +44 -0
- package/dist/lib/brushes/presets/round/round.js +3 -0
- package/dist/lib/brushes/presets/round/soft-round.js +3 -0
- package/dist/lib/brushes/presets/sized/index.d.ts +20 -0
- package/dist/lib/brushes/presets/sized/index.js +30 -0
- package/dist/lib/brushes/presets/sketching/index.d.ts +14 -0
- package/dist/lib/brushes/presets/sketching/index.js +107 -0
- package/dist/lib/brushes/presets/square/index.d.ts +7 -0
- package/dist/lib/brushes/presets/square/index.js +22 -0
- package/dist/lib/brushes/presets/square/square.js +3 -0
- package/dist/lib/brushes/shapeImage/index.d.ts +40 -0
- package/dist/lib/brushes/shapeImage/index.js +61 -0
- package/dist/lib/brushes/types/index.d.ts +243 -0
- package/dist/lib/brushes/types/index.js +20 -0
- package/dist/lib/classes/BackgroundWork/index.d.ts +55 -0
- package/dist/lib/classes/BackgroundWork/index.js +97 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +91 -0
- package/dist/lib/classes/BrushSettings/index.js +152 -0
- package/dist/lib/classes/CustomPresets/index.d.ts +53 -0
- package/dist/lib/classes/CustomPresets/index.js +71 -0
- package/dist/lib/classes/ExternalSheets/index.d.ts +64 -0
- package/dist/lib/classes/ExternalSheets/index.js +112 -0
- package/dist/lib/classes/GestureTracker/index.d.ts +62 -0
- package/dist/lib/classes/GestureTracker/index.js +122 -0
- package/dist/lib/classes/History/index.d.ts +83 -0
- package/dist/lib/classes/History/index.js +126 -0
- package/dist/lib/classes/HistoryGuard/index.d.ts +58 -0
- package/dist/lib/classes/HistoryGuard/index.js +97 -0
- package/dist/lib/classes/InputSettings/index.d.ts +74 -0
- package/dist/lib/classes/InputSettings/index.js +101 -0
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +48 -0
- package/dist/lib/classes/KeyboardShortcuts/index.js +137 -0
- package/dist/lib/classes/Layer/index.d.ts +86 -0
- package/dist/lib/classes/Layer/index.js +111 -0
- package/dist/lib/classes/LayerAutosave/index.d.ts +180 -0
- package/dist/lib/classes/LayerAutosave/index.js +346 -0
- package/dist/lib/classes/LayerStack/index.d.ts +177 -0
- package/dist/lib/classes/LayerStack/index.js +394 -0
- package/dist/lib/classes/PaintCanvas/index.d.ts +537 -0
- package/dist/lib/classes/PaintCanvas/index.js +965 -0
- package/dist/lib/classes/StrokeLog/index.d.ts +149 -0
- package/dist/lib/classes/StrokeLog/index.js +257 -0
- package/dist/lib/classes/Viewport/index.d.ts +108 -0
- package/dist/lib/classes/Viewport/index.js +194 -0
- package/dist/lib/document/index.d.ts +17 -0
- package/dist/lib/document/index.js +25 -0
- package/dist/lib/document/schema.d.ts +132 -0
- package/dist/lib/document/schema.js +110 -0
- package/dist/lib/i18n/index.d.ts +131 -0
- package/dist/lib/i18n/index.js +277 -0
- package/dist/lib/image/index.d.ts +90 -0
- package/dist/lib/image/index.js +147 -0
- package/dist/lib/messages/PatchSender/index.d.ts +75 -0
- package/dist/lib/messages/PatchSender/index.js +125 -0
- package/dist/lib/messages/index.d.ts +26 -0
- package/dist/lib/messages/index.js +59 -0
- package/dist/lib/messages/schema.d.ts +1775 -0
- package/dist/lib/messages/schema.js +192 -0
- package/dist/lib/presets/index.d.ts +32 -0
- package/dist/lib/presets/index.js +35 -0
- package/dist/lib/presets/schema.d.ts +753 -0
- package/dist/lib/presets/schema.js +181 -0
- package/dist/lib/rendering/BrushTip/index.d.ts +63 -0
- package/dist/lib/rendering/BrushTip/index.js +112 -0
- package/dist/lib/rendering/checkerboard/index.d.ts +22 -0
- package/dist/lib/rendering/checkerboard/index.js +31 -0
- package/dist/lib/rendering/compositing/index.d.ts +64 -0
- package/dist/lib/rendering/compositing/index.js +124 -0
- package/dist/lib/rendering/cursor/index.d.ts +14 -0
- package/dist/lib/rendering/cursor/index.js +39 -0
- package/dist/lib/rendering/cursors/index.d.ts +18 -0
- package/dist/lib/rendering/cursors/index.js +23 -0
- package/dist/lib/rendering/maskOutline/index.d.ts +22 -0
- package/dist/lib/rendering/maskOutline/index.js +78 -0
- package/dist/lib/rendering/outline/index.d.ts +36 -0
- package/dist/lib/rendering/outline/index.js +120 -0
- package/dist/lib/rendering/sample/index.d.ts +55 -0
- package/dist/lib/rendering/sample/index.js +83 -0
- package/dist/lib/rendering/screenshot/index.d.ts +67 -0
- package/dist/lib/rendering/screenshot/index.js +96 -0
- package/dist/lib/rendering/transformBox/index.d.ts +10 -0
- package/dist/lib/rendering/transformBox/index.js +57 -0
- package/dist/lib/selection/Lasso/index.d.ts +70 -0
- package/dist/lib/selection/Lasso/index.js +156 -0
- package/dist/lib/selection/Selection/index.d.ts +86 -0
- package/dist/lib/selection/Selection/index.js +228 -0
- package/dist/lib/selection/geometry/index.d.ts +37 -0
- package/dist/lib/selection/geometry/index.js +57 -0
- package/dist/lib/storage/index.d.ts +63 -0
- package/dist/lib/storage/index.js +114 -0
- package/dist/lib/transform/Transformer/index.d.ts +124 -0
- package/dist/lib/transform/Transformer/index.js +286 -0
- package/dist/lib/transform/geometry/index.d.ts +95 -0
- package/dist/lib/transform/geometry/index.js +258 -0
- package/dist/lib/utils/array/index.d.ts +10 -0
- package/dist/lib/utils/array/index.js +21 -0
- package/dist/lib/utils/gestures/index.d.ts +35 -0
- package/dist/lib/utils/gestures/index.js +81 -0
- package/dist/lib/utils/navigation/index.d.ts +51 -0
- package/dist/lib/utils/navigation/index.js +59 -0
- package/dist/lib/utils/random/index.d.ts +18 -0
- package/dist/lib/utils/random/index.js +27 -0
- package/dist/lib/utils/shortcuts/index.d.ts +74 -0
- package/dist/lib/utils/shortcuts/index.js +189 -0
- package/dist/lib/validation/validators.generated.d.ts +27 -0
- package/dist/lib/validation/validators.generated.js +19826 -0
- package/dist/locales/en-GB.js +181 -0
- package/dist/locales/en-US.js +181 -0
- package/dist/locales/fr.js +181 -0
- package/dist/locales/ja.js +181 -0
- package/dist/locales/ko.js +181 -0
- package/dist/locales/zh-Hans.js +181 -0
- package/dist/locales/zh-Hant.js +181 -0
- package/dist/package.js +3 -0
- package/dist/testing/app.d.ts +7 -0
- package/dist/testing/app.js +16 -0
- package/dist/testing/elements.d.ts +42 -0
- package/dist/testing/elements.js +58 -0
- package/dist/testing/fakeCanvas.d.ts +21 -0
- package/dist/testing/fakeCanvas.js +26 -0
- package/dist/testing/layerStore.d.ts +26 -0
- package/dist/testing/layerStore.js +43 -0
- package/dist/testing/locks.d.ts +15 -0
- package/dist/testing/locks.js +41 -0
- package/dist/testing/recordingCanvas.d.ts +14 -0
- package/dist/testing/recordingCanvas.js +28 -0
- package/dist/testing/recordingContext.d.ts +10 -0
- package/dist/testing/recordingContext.js +19 -0
- package/dist/ui/ChangeController/index.d.ts +40 -0
- package/dist/ui/ChangeController/index.js +62 -0
- package/dist/ui/base/index.d.ts +69 -0
- package/dist/ui/base/index.js +94 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +249 -0
- package/dist/ui/elements/PaintCanvas/index.js +584 -0
- package/dist/ui/elements/PaintCanvas/style.js +3 -0
- package/dist/ui/elements/PcBrushConfig/index.d.ts +130 -0
- package/dist/ui/elements/PcBrushConfig/index.js +465 -0
- package/dist/ui/elements/PcBrushConfig/style.js +3 -0
- package/dist/ui/elements/PcBrushMenu/index.d.ts +121 -0
- package/dist/ui/elements/PcBrushMenu/index.js +362 -0
- package/dist/ui/elements/PcBrushMenu/style.js +3 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +71 -0
- package/dist/ui/elements/PcBrushTest/index.js +136 -0
- package/dist/ui/elements/PcBrushTest/style.js +3 -0
- package/dist/ui/elements/PcButton/index.d.ts +57 -0
- package/dist/ui/elements/PcButton/index.js +124 -0
- package/dist/ui/elements/PcButtonGroup/index.d.ts +44 -0
- package/dist/ui/elements/PcButtonGroup/index.js +93 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +356 -0
- package/dist/ui/elements/PcCanvas/index.js +1004 -0
- package/dist/ui/elements/PcCanvas/style.js +3 -0
- package/dist/ui/elements/PcCanvasMap/index.d.ts +96 -0
- package/dist/ui/elements/PcCanvasMap/index.js +187 -0
- package/dist/ui/elements/PcCanvasMap/style.js +3 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +72 -0
- package/dist/ui/elements/PcContextMenu/index.js +148 -0
- package/dist/ui/elements/PcDialog/index.d.ts +63 -0
- package/dist/ui/elements/PcDialog/index.js +114 -0
- package/dist/ui/elements/PcIcon/index.d.ts +36 -0
- package/dist/ui/elements/PcIcon/index.js +43 -0
- package/dist/ui/elements/PcIconToggle/index.d.ts +46 -0
- package/dist/ui/elements/PcIconToggle/index.js +87 -0
- package/dist/ui/elements/PcInfoTip/index.d.ts +40 -0
- package/dist/ui/elements/PcInfoTip/index.js +95 -0
- package/dist/ui/elements/PcInputSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcInputSettings/index.js +154 -0
- package/dist/ui/elements/PcLayerItem/index.d.ts +221 -0
- package/dist/ui/elements/PcLayerItem/index.js +561 -0
- package/dist/ui/elements/PcLayerItem/style.js +3 -0
- package/dist/ui/elements/PcLayersMenu/index.d.ts +108 -0
- package/dist/ui/elements/PcLayersMenu/index.js +208 -0
- package/dist/ui/elements/PcLayersMenu/style.js +3 -0
- package/dist/ui/elements/PcMatch/index.d.ts +31 -0
- package/dist/ui/elements/PcMatch/index.js +58 -0
- package/dist/ui/elements/PcPanel/index.d.ts +43 -0
- package/dist/ui/elements/PcPanel/index.js +51 -0
- package/dist/ui/elements/PcPopover/index.d.ts +48 -0
- package/dist/ui/elements/PcPopover/index.js +86 -0
- package/dist/ui/elements/PcSlider/index.d.ts +57 -0
- package/dist/ui/elements/PcSlider/index.js +88 -0
- package/dist/ui/elements/PcStats/index.d.ts +54 -0
- package/dist/ui/elements/PcStats/index.js +144 -0
- package/dist/ui/elements/PcThemePicker/index.d.ts +56 -0
- package/dist/ui/elements/PcThemePicker/index.js +134 -0
- package/dist/ui/elements/PcToast/index.d.ts +58 -0
- package/dist/ui/elements/PcToast/index.js +110 -0
- package/dist/ui/elements/PcToggleCheckbox/index.d.ts +37 -0
- package/dist/ui/elements/PcToggleCheckbox/index.js +105 -0
- package/dist/ui/elements/PcToolSettings/index.d.ts +128 -0
- package/dist/ui/elements/PcToolSettings/index.js +549 -0
- package/dist/ui/elements/PcToolSettings/style.js +3 -0
- package/dist/ui/elements/PcToolbar/index.d.ts +71 -0
- package/dist/ui/elements/PcToolbar/index.js +190 -0
- package/dist/ui/elements/PcToolbar/style.js +3 -0
- package/dist/ui/icons/images/brush.js +3 -0
- package/dist/ui/icons/images/circle-mask.js +3 -0
- package/dist/ui/icons/images/clipboard.js +3 -0
- package/dist/ui/icons/images/cursor.js +3 -0
- package/dist/ui/icons/images/eraser.js +3 -0
- package/dist/ui/icons/images/eyedropper.js +3 -0
- package/dist/ui/icons/images/flip-horizontal.js +3 -0
- package/dist/ui/icons/images/flip-vertical.js +3 -0
- package/dist/ui/icons/images/focus-pen.js +3 -0
- package/dist/ui/icons/images/freehand-mask.js +3 -0
- package/dist/ui/icons/images/hidden.js +3 -0
- package/dist/ui/icons/images/layers.js +3 -0
- package/dist/ui/icons/images/linked.js +3 -0
- package/dist/ui/icons/images/locked.js +3 -0
- package/dist/ui/icons/images/moon.js +3 -0
- package/dist/ui/icons/images/pan.js +3 -0
- package/dist/ui/icons/images/polygon-mask.js +3 -0
- package/dist/ui/icons/images/pressure.js +3 -0
- package/dist/ui/icons/images/randomise.js +3 -0
- package/dist/ui/icons/images/rectangle-mask.js +3 -0
- package/dist/ui/icons/images/redo.js +3 -0
- package/dist/ui/icons/images/rotate-90-left.js +3 -0
- package/dist/ui/icons/images/rotate-90-right.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-quad.js +3 -0
- package/dist/ui/icons/images/smoothing-linear.js +3 -0
- package/dist/ui/icons/images/star.js +3 -0
- package/dist/ui/icons/images/sun.js +3 -0
- package/dist/ui/icons/images/tilt.js +3 -0
- package/dist/ui/icons/images/undo.js +3 -0
- package/dist/ui/icons/images/unlinked.js +3 -0
- package/dist/ui/icons/images/unlocked.js +3 -0
- package/dist/ui/icons/images/visible.js +3 -0
- package/dist/ui/icons/images/zoom-in.js +3 -0
- package/dist/ui/icons/images/zoom-out.js +3 -0
- package/dist/ui/icons/images/zoom.js +3 -0
- package/dist/ui/icons/index.d.ts +42 -0
- package/dist/ui/icons/index.js +130 -0
- package/dist/ui/index.d.ts +21 -0
- package/dist/ui/index.js +22 -0
- package/dist/ui/layout/index.d.ts +88 -0
- package/dist/ui/layout/index.js +96 -0
- package/dist/ui/styles/index.d.ts +13 -0
- package/dist/ui/styles/index.js +195 -0
- package/dist/ui/tokens/index.d.ts +29 -0
- package/dist/ui/tokens/index.js +114 -0
- package/locales/en-GB.json +179 -0
- package/locales/en-US.json +179 -0
- package/locales/fr.json +179 -0
- package/locales/ja.json +179 -0
- package/locales/ko.json +179 -0
- package/locales/zh-Hans.json +179 -0
- package/locales/zh-Hant.json +179 -0
- package/package.json +97 -3
|
@@ -0,0 +1,3 @@
|
|
|
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";
|
|
3
|
+
export { style_default as default };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { PcElement } from "../../base";
|
|
2
|
+
import type PaintCanvas from "../../../lib/classes/PaintCanvas";
|
|
3
|
+
import type { Insets, Rect } from "../../../lib/classes/Viewport";
|
|
4
|
+
/** Largest map width or height in CSS pixels */
|
|
5
|
+
export declare const MAP_SIZE = 90;
|
|
6
|
+
/** Shortest time between thumbnail redraws, in milliseconds (redrawing the whole canvas is slow) */
|
|
7
|
+
export declare const MAP_REDRAW_INTERVAL = 250;
|
|
8
|
+
/**
|
|
9
|
+
* Convert the visible area into percentage offsets and sizes within the map, clamped to the canvas.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* viewportFrame({ x: 500, y: 0, width: 1000, height: 1000 }, 2000, 2000);
|
|
13
|
+
* // { left: 25, top: 0, width: 50, height: 50 }
|
|
14
|
+
*/
|
|
15
|
+
export declare const viewportFrame: (viewport: Rect, canvasWidth: number, canvasHeight: number) => {
|
|
16
|
+
left: number;
|
|
17
|
+
top: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Canvas map: a small live picture of the whole canvas with a frame showing the part the user can see.
|
|
23
|
+
* Clicking or dragging on the map moves the view there.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* html`<pc-canvas-map .app=${app}></pc-canvas-map>`
|
|
27
|
+
*/
|
|
28
|
+
export declare class PcCanvasMap extends PcElement {
|
|
29
|
+
static properties: {
|
|
30
|
+
app: {
|
|
31
|
+
attribute: boolean;
|
|
32
|
+
};
|
|
33
|
+
insets: {
|
|
34
|
+
attribute: boolean;
|
|
35
|
+
};
|
|
36
|
+
translations: {
|
|
37
|
+
attribute: boolean;
|
|
38
|
+
};
|
|
39
|
+
icons: {
|
|
40
|
+
attribute: boolean;
|
|
41
|
+
};
|
|
42
|
+
theme: {
|
|
43
|
+
type: StringConstructor;
|
|
44
|
+
reflect: boolean;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
static styles: import("lit").CSSResultGroup[];
|
|
48
|
+
app?: PaintCanvas;
|
|
49
|
+
/**
|
|
50
|
+
* Space covered by the interface on each side (CSS pixels), kept clear when fitting or centring the canvas. Without
|
|
51
|
+
* it, a toolbar on the left and (with the interface shown) a panel taking 37.5% on the right are assumed.
|
|
52
|
+
*/
|
|
53
|
+
insets?: Insets;
|
|
54
|
+
/** The insets in effect (see `insets`) */
|
|
55
|
+
private get activeInsets();
|
|
56
|
+
/** Device pixels per CSS pixel (replaceable for tests) */
|
|
57
|
+
pixelRatio: () => number;
|
|
58
|
+
private changes;
|
|
59
|
+
private watchedLayers?;
|
|
60
|
+
/** Other people's drawings in a session, which also change the thumbnail */
|
|
61
|
+
private watchedRemote?;
|
|
62
|
+
private readonly onLayersChange;
|
|
63
|
+
private lastDrawn;
|
|
64
|
+
private pendingDraw?;
|
|
65
|
+
private dragging;
|
|
66
|
+
/** The thumbnail canvas */
|
|
67
|
+
get canvas(): HTMLCanvasElement | null;
|
|
68
|
+
disconnectedCallback(): void;
|
|
69
|
+
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
70
|
+
updated(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Listen for changes to a layer stack (replacing any previous one).
|
|
73
|
+
*/
|
|
74
|
+
private watchLayers;
|
|
75
|
+
/**
|
|
76
|
+
* Redraw the thumbnail soon, no more often than `MAP_REDRAW_INTERVAL`, once drawing pauses (see `BackgroundWork`).
|
|
77
|
+
*/
|
|
78
|
+
scheduleThumbnail(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Draw the whole canvas into the map now.
|
|
81
|
+
*/
|
|
82
|
+
drawThumbnail(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Move the view to the canvas point under a pointer on the map.
|
|
85
|
+
*/
|
|
86
|
+
private moveViewTo;
|
|
87
|
+
/**
|
|
88
|
+
* Start moving the view.
|
|
89
|
+
*/
|
|
90
|
+
private onPointerDown;
|
|
91
|
+
/**
|
|
92
|
+
* Keep moving the view while dragging.
|
|
93
|
+
*/
|
|
94
|
+
private onPointerMove;
|
|
95
|
+
render(): import("lit").TemplateResult<1>;
|
|
96
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import style_default from "./style.js";
|
|
2
|
+
import { NO_TEXT_SELECTION_CSS } from "../../styles/index.js";
|
|
3
|
+
import { PcElement, defineElement } from "../../base/index.js";
|
|
4
|
+
import ChangeController from "../../ChangeController/index.js";
|
|
5
|
+
import { interfaceInsets } from "../PcCanvas/index.js";
|
|
6
|
+
import { drawFlattened } from "../../../lib/rendering/compositing/index.js";
|
|
7
|
+
import { fitSize } from "../../../lib/rendering/checkerboard/index.js";
|
|
8
|
+
import { html, unsafeCSS } from "lit";
|
|
9
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
10
|
+
// src/ui/elements/PcCanvasMap/index.ts
|
|
11
|
+
/** Largest map width or height in CSS pixels */
|
|
12
|
+
var MAP_SIZE = 90;
|
|
13
|
+
/** Shortest time between thumbnail redraws, in milliseconds (redrawing the whole canvas is slow) */
|
|
14
|
+
var MAP_REDRAW_INTERVAL = 250;
|
|
15
|
+
/**
|
|
16
|
+
* Convert the visible area into percentage offsets and sizes within the map, clamped to the canvas.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* viewportFrame({ x: 500, y: 0, width: 1000, height: 1000 }, 2000, 2000);
|
|
20
|
+
* // { left: 25, top: 0, width: 50, height: 50 }
|
|
21
|
+
*/
|
|
22
|
+
var viewportFrame = (viewport, canvasWidth, canvasHeight) => {
|
|
23
|
+
const clampPercent = (value) => Math.max(0, Math.min(100, value));
|
|
24
|
+
const left = clampPercent(viewport.x / canvasWidth * 100);
|
|
25
|
+
const top = clampPercent(viewport.y / canvasHeight * 100);
|
|
26
|
+
return {
|
|
27
|
+
left,
|
|
28
|
+
top,
|
|
29
|
+
width: clampPercent((viewport.x + viewport.width) / canvasWidth * 100) - left,
|
|
30
|
+
height: clampPercent((viewport.y + viewport.height) / canvasHeight * 100) - top
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Canvas map: a small live picture of the whole canvas with a frame showing the part the user can see.
|
|
35
|
+
* Clicking or dragging on the map moves the view there.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* html`<pc-canvas-map .app=${app}></pc-canvas-map>`
|
|
39
|
+
*/
|
|
40
|
+
var PcCanvasMap = class extends PcElement {
|
|
41
|
+
static properties = {
|
|
42
|
+
...PcElement.properties,
|
|
43
|
+
app: { attribute: false },
|
|
44
|
+
insets: { attribute: false }
|
|
45
|
+
};
|
|
46
|
+
static styles = [
|
|
47
|
+
PcElement.styles,
|
|
48
|
+
unsafeCSS(NO_TEXT_SELECTION_CSS),
|
|
49
|
+
unsafeCSS(style_default)
|
|
50
|
+
];
|
|
51
|
+
/** The insets in effect (see `insets`) */
|
|
52
|
+
get activeInsets() {
|
|
53
|
+
const app = this.app;
|
|
54
|
+
return this.insets ?? interfaceInsets(app.viewport.width, app.interfaceVisible);
|
|
55
|
+
}
|
|
56
|
+
/** Device pixels per CSS pixel (replaceable for tests) */
|
|
57
|
+
pixelRatio = () => window.devicePixelRatio || 1;
|
|
58
|
+
changes = new ChangeController(this, () => [this.app, this.app?.viewport]);
|
|
59
|
+
watchedLayers;
|
|
60
|
+
/** Other people's drawings in a session, which also change the thumbnail */
|
|
61
|
+
watchedRemote;
|
|
62
|
+
onLayersChange = () => this.scheduleThumbnail();
|
|
63
|
+
lastDrawn = 0;
|
|
64
|
+
pendingDraw;
|
|
65
|
+
dragging = false;
|
|
66
|
+
/** The thumbnail canvas */
|
|
67
|
+
get canvas() {
|
|
68
|
+
return this.renderRoot.querySelector("canvas");
|
|
69
|
+
}
|
|
70
|
+
disconnectedCallback() {
|
|
71
|
+
super.disconnectedCallback();
|
|
72
|
+
this.watchLayers(void 0);
|
|
73
|
+
clearTimeout(this.pendingDraw);
|
|
74
|
+
this.pendingDraw = void 0;
|
|
75
|
+
}
|
|
76
|
+
willUpdate(changed) {
|
|
77
|
+
super.willUpdate(changed);
|
|
78
|
+
const app = this.app;
|
|
79
|
+
if (!app) return;
|
|
80
|
+
const size = fitSize(app.width, app.height, 90, 90);
|
|
81
|
+
this.style.width = `${size.width}px`;
|
|
82
|
+
this.style.height = `${size.height}px`;
|
|
83
|
+
}
|
|
84
|
+
updated() {
|
|
85
|
+
const layers = this.app?.layers;
|
|
86
|
+
if (layers !== this.watchedLayers) {
|
|
87
|
+
this.watchLayers(layers);
|
|
88
|
+
this.drawThumbnail();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Listen for changes to a layer stack (replacing any previous one).
|
|
93
|
+
*/
|
|
94
|
+
watchLayers(layers) {
|
|
95
|
+
this.watchedLayers?.removeEventListener("change", this.onLayersChange);
|
|
96
|
+
this.watchedLayers = layers;
|
|
97
|
+
layers?.addEventListener("change", this.onLayersChange);
|
|
98
|
+
const remote = layers ? this.app?.external : void 0;
|
|
99
|
+
this.watchedRemote?.removeEventListener("change", this.onLayersChange);
|
|
100
|
+
this.watchedRemote = remote;
|
|
101
|
+
remote?.addEventListener("change", this.onLayersChange);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Redraw the thumbnail soon, no more often than `MAP_REDRAW_INTERVAL`, once drawing pauses (see `BackgroundWork`).
|
|
105
|
+
*/
|
|
106
|
+
scheduleThumbnail() {
|
|
107
|
+
if (this.pendingDraw) return;
|
|
108
|
+
const wait = Math.max(0, this.lastDrawn + 250 - Date.now());
|
|
109
|
+
this.pendingDraw = setTimeout(() => {
|
|
110
|
+
this.pendingDraw = void 0;
|
|
111
|
+
this.app?.background.run("map", () => this.drawThumbnail());
|
|
112
|
+
}, wait);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Draw the whole canvas into the map now.
|
|
116
|
+
*/
|
|
117
|
+
drawThumbnail() {
|
|
118
|
+
const app = this.app;
|
|
119
|
+
const canvas = this.canvas;
|
|
120
|
+
if (!app || !canvas) return;
|
|
121
|
+
const size = fitSize(app.width, app.height, 90, 90);
|
|
122
|
+
const ratio = this.pixelRatio();
|
|
123
|
+
canvas.width = Math.round(size.width * ratio);
|
|
124
|
+
canvas.height = Math.round(size.height * ratio);
|
|
125
|
+
const ctx = canvas.getContext("2d");
|
|
126
|
+
if (!ctx) return;
|
|
127
|
+
drawFlattened(ctx, app.drawables, app.width, app.height, canvas.width, canvas.height);
|
|
128
|
+
this.lastDrawn = Date.now();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Move the view to the canvas point under a pointer on the map.
|
|
132
|
+
*/
|
|
133
|
+
moveViewTo(event) {
|
|
134
|
+
const app = this.app;
|
|
135
|
+
if (!app) return;
|
|
136
|
+
const rect = this.getBoundingClientRect();
|
|
137
|
+
const x = (event.clientX - rect.left) / rect.width * app.width;
|
|
138
|
+
const y = (event.clientY - rect.top) / rect.height * app.height;
|
|
139
|
+
app.viewport.centreOn({
|
|
140
|
+
x,
|
|
141
|
+
y
|
|
142
|
+
}, this.activeInsets);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Start moving the view.
|
|
146
|
+
*/
|
|
147
|
+
onPointerDown(event) {
|
|
148
|
+
this.dragging = true;
|
|
149
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
150
|
+
this.moveViewTo(event);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Keep moving the view while dragging.
|
|
154
|
+
*/
|
|
155
|
+
onPointerMove(event) {
|
|
156
|
+
if (this.dragging) this.moveViewTo(event);
|
|
157
|
+
}
|
|
158
|
+
render() {
|
|
159
|
+
const app = this.app;
|
|
160
|
+
const frame = app ? viewportFrame(app.viewport.visibleRect(), app.width, app.height) : {
|
|
161
|
+
left: 0,
|
|
162
|
+
top: 0,
|
|
163
|
+
width: 100,
|
|
164
|
+
height: 100
|
|
165
|
+
};
|
|
166
|
+
return html`
|
|
167
|
+
<canvas
|
|
168
|
+
aria-label=${this.t("paintCanvas.canvas.map")}
|
|
169
|
+
@pointerdown=${this.onPointerDown}
|
|
170
|
+
@pointermove=${this.onPointerMove}
|
|
171
|
+
@pointerup=${() => this.dragging = false}
|
|
172
|
+
@pointercancel=${() => this.dragging = false}
|
|
173
|
+
></canvas>
|
|
174
|
+
<div
|
|
175
|
+
class="frame"
|
|
176
|
+
style=${styleMap({
|
|
177
|
+
left: `${frame.left}%`,
|
|
178
|
+
top: `${frame.top}%`,
|
|
179
|
+
width: `${frame.width}%`,
|
|
180
|
+
height: `${frame.height}%`
|
|
181
|
+
})}
|
|
182
|
+
></div>
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
defineElement("pc-canvas-map", PcCanvasMap);
|
|
187
|
+
export { MAP_REDRAW_INTERVAL, MAP_SIZE, PcCanvasMap, viewportFrame };
|
|
@@ -0,0 +1,3 @@
|
|
|
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";
|
|
3
|
+
export { style_default as default };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type PropertyValues } from "lit";
|
|
2
|
+
import { PcElement } from "../../base";
|
|
3
|
+
/** An item in a context menu */
|
|
4
|
+
export interface MenuItem {
|
|
5
|
+
/** What `pc-select` gives when it's chosen */
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** Shown in the cancelling colour, e.g. for deleting */
|
|
10
|
+
danger?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A menu of actions, e.g. for a layer. While `open`, it shows its items at `x`, `y` (in the page's viewport) and takes
|
|
14
|
+
* keyboard focus: arrow keys, Home and End move between items, Enter or Space chooses one, and Escape or Tab closes it.
|
|
15
|
+
* Fires `pc-select` with the chosen item's value, and `pc-close` when it should close (after choosing, Escape, Tab, or a
|
|
16
|
+
* press outside it). The implementer sets `open` to false in response.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* html`<pc-context-menu open .x=${10} .y=${20} label="Layer actions" .items=${items}
|
|
20
|
+
* @pc-select=${(e) => act(e.detail.value)} @pc-close=${close}></pc-context-menu>`
|
|
21
|
+
*/
|
|
22
|
+
export declare class PcContextMenu extends PcElement {
|
|
23
|
+
static properties: {
|
|
24
|
+
open: {
|
|
25
|
+
type: BooleanConstructor;
|
|
26
|
+
reflect: boolean;
|
|
27
|
+
};
|
|
28
|
+
items: {
|
|
29
|
+
attribute: boolean;
|
|
30
|
+
};
|
|
31
|
+
label: {
|
|
32
|
+
type: StringConstructor;
|
|
33
|
+
};
|
|
34
|
+
x: {
|
|
35
|
+
type: NumberConstructor;
|
|
36
|
+
};
|
|
37
|
+
y: {
|
|
38
|
+
type: NumberConstructor;
|
|
39
|
+
};
|
|
40
|
+
translations: {
|
|
41
|
+
attribute: boolean;
|
|
42
|
+
};
|
|
43
|
+
icons: {
|
|
44
|
+
attribute: boolean;
|
|
45
|
+
};
|
|
46
|
+
theme: {
|
|
47
|
+
type: StringConstructor;
|
|
48
|
+
reflect: boolean;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
static styles: import("lit").CSSResultGroup[];
|
|
52
|
+
open: boolean;
|
|
53
|
+
items: readonly MenuItem[];
|
|
54
|
+
/** Names the menu for screen readers */
|
|
55
|
+
label: string;
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
private readonly onPointerDown;
|
|
59
|
+
constructor();
|
|
60
|
+
connectedCallback(): void;
|
|
61
|
+
disconnectedCallback(): void;
|
|
62
|
+
updated(changed: PropertyValues<this>): void;
|
|
63
|
+
/** The enabled items' buttons */
|
|
64
|
+
private buttons;
|
|
65
|
+
/** Tell listeners the menu should close */
|
|
66
|
+
close(): void;
|
|
67
|
+
/** Choose an item, then close */
|
|
68
|
+
private choose;
|
|
69
|
+
/** Move focus between items, or close */
|
|
70
|
+
private onKeyDown;
|
|
71
|
+
render(): import("lit").TemplateResult<1>;
|
|
72
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { PcElement, defineElement, emit } from "../../base/index.js";
|
|
2
|
+
import { css, html, nothing } from "lit";
|
|
3
|
+
// src/ui/elements/PcContextMenu/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* A menu of actions, e.g. for a layer. While `open`, it shows its items at `x`, `y` (in the page's viewport) and takes
|
|
6
|
+
* keyboard focus: arrow keys, Home and End move between items, Enter or Space chooses one, and Escape or Tab closes it.
|
|
7
|
+
* Fires `pc-select` with the chosen item's value, and `pc-close` when it should close (after choosing, Escape, Tab, or a
|
|
8
|
+
* press outside it). The implementer sets `open` to false in response.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* html`<pc-context-menu open .x=${10} .y=${20} label="Layer actions" .items=${items}
|
|
12
|
+
* @pc-select=${(e) => act(e.detail.value)} @pc-close=${close}></pc-context-menu>`
|
|
13
|
+
*/
|
|
14
|
+
var PcContextMenu = class extends PcElement {
|
|
15
|
+
static properties = {
|
|
16
|
+
...PcElement.properties,
|
|
17
|
+
open: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
reflect: true
|
|
20
|
+
},
|
|
21
|
+
items: { attribute: false },
|
|
22
|
+
label: { type: String },
|
|
23
|
+
x: { type: Number },
|
|
24
|
+
y: { type: Number }
|
|
25
|
+
};
|
|
26
|
+
static styles = [PcElement.styles, css`
|
|
27
|
+
:host {
|
|
28
|
+
position: fixed;
|
|
29
|
+
z-index: 10;
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
:host([open]) {
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
[role="menu"] {
|
|
36
|
+
display: flex;
|
|
37
|
+
min-width: 160px;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
padding: var(--pc-space-1) 0;
|
|
40
|
+
border-radius: var(--pc-radius);
|
|
41
|
+
background: var(--pc-surface-raised);
|
|
42
|
+
box-shadow: var(--pc-shadow);
|
|
43
|
+
}
|
|
44
|
+
button {
|
|
45
|
+
padding: var(--pc-space-2) var(--pc-space-3);
|
|
46
|
+
border: 0;
|
|
47
|
+
background: transparent;
|
|
48
|
+
color: var(--pc-text);
|
|
49
|
+
font-family: var(--pc-font);
|
|
50
|
+
font-size: var(--pc-font-size);
|
|
51
|
+
text-align: start;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
button:hover:not(:disabled),
|
|
55
|
+
button:focus-visible {
|
|
56
|
+
background: var(--pc-surface-hover);
|
|
57
|
+
outline: none;
|
|
58
|
+
}
|
|
59
|
+
button:focus-visible {
|
|
60
|
+
box-shadow: inset 0 0 0 var(--pc-focus-width) var(--pc-focus);
|
|
61
|
+
}
|
|
62
|
+
button.danger {
|
|
63
|
+
color: var(--pc-dismiss-text);
|
|
64
|
+
}
|
|
65
|
+
button:disabled {
|
|
66
|
+
color: var(--pc-text-muted);
|
|
67
|
+
cursor: default;
|
|
68
|
+
}
|
|
69
|
+
`];
|
|
70
|
+
onPointerDown = (event) => {
|
|
71
|
+
if (this.open && !event.composedPath().includes(this)) this.close();
|
|
72
|
+
};
|
|
73
|
+
constructor() {
|
|
74
|
+
super();
|
|
75
|
+
this.open = false;
|
|
76
|
+
this.items = [];
|
|
77
|
+
this.label = "";
|
|
78
|
+
this.x = 0;
|
|
79
|
+
this.y = 0;
|
|
80
|
+
}
|
|
81
|
+
connectedCallback() {
|
|
82
|
+
super.connectedCallback();
|
|
83
|
+
document.addEventListener("pointerdown", this.onPointerDown, true);
|
|
84
|
+
}
|
|
85
|
+
disconnectedCallback() {
|
|
86
|
+
super.disconnectedCallback();
|
|
87
|
+
document.removeEventListener("pointerdown", this.onPointerDown, true);
|
|
88
|
+
}
|
|
89
|
+
updated(changed) {
|
|
90
|
+
this.style.left = `${this.x}px`;
|
|
91
|
+
this.style.top = `${this.y}px`;
|
|
92
|
+
if (changed.has("open") && this.open) this.buttons()[0]?.focus();
|
|
93
|
+
}
|
|
94
|
+
/** The enabled items' buttons */
|
|
95
|
+
buttons() {
|
|
96
|
+
return [...this.renderRoot.querySelectorAll("button")].filter((button) => !button.disabled);
|
|
97
|
+
}
|
|
98
|
+
/** Tell listeners the menu should close */
|
|
99
|
+
close() {
|
|
100
|
+
emit(this, "pc-close");
|
|
101
|
+
}
|
|
102
|
+
/** Choose an item, then close */
|
|
103
|
+
choose(item) {
|
|
104
|
+
emit(this, "pc-select", { value: item.value });
|
|
105
|
+
this.close();
|
|
106
|
+
}
|
|
107
|
+
/** Move focus between items, or close */
|
|
108
|
+
onKeyDown(event) {
|
|
109
|
+
const buttons = this.buttons();
|
|
110
|
+
const index = buttons.indexOf(this.renderRoot.activeElement);
|
|
111
|
+
const last = buttons.length - 1;
|
|
112
|
+
const target = {
|
|
113
|
+
ArrowDown: index >= last ? 0 : index + 1,
|
|
114
|
+
ArrowUp: index <= 0 ? last : index - 1,
|
|
115
|
+
Home: 0,
|
|
116
|
+
End: last
|
|
117
|
+
}[event.key];
|
|
118
|
+
if (target !== void 0) {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
buttons[target]?.focus();
|
|
121
|
+
} else if (event.key === "Escape" || event.key === "Tab") {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
this.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
render() {
|
|
127
|
+
return html`<div
|
|
128
|
+
part="menu"
|
|
129
|
+
role="menu"
|
|
130
|
+
aria-label=${this.label}
|
|
131
|
+
@keydown=${this.onKeyDown}
|
|
132
|
+
>
|
|
133
|
+
${this.items.map((item) => html`<button
|
|
134
|
+
part="item"
|
|
135
|
+
type="button"
|
|
136
|
+
role="menuitem"
|
|
137
|
+
class=${item.danger ? "danger" : nothing}
|
|
138
|
+
?disabled=${item.disabled}
|
|
139
|
+
data-value=${item.value}
|
|
140
|
+
@click=${() => this.choose(item)}
|
|
141
|
+
>
|
|
142
|
+
${item.label}
|
|
143
|
+
</button>`)}
|
|
144
|
+
</div>`;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
defineElement("pc-context-menu", PcContextMenu);
|
|
148
|
+
export { PcContextMenu };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type PropertyValues } from "lit";
|
|
2
|
+
import { PcElement } from "../../base";
|
|
3
|
+
import "../PcButton";
|
|
4
|
+
import type { ButtonVariant } from "../PcButton";
|
|
5
|
+
/** A button in a dialog */
|
|
6
|
+
export interface DialogChoice {
|
|
7
|
+
/** What `pc-choose` gives when it's chosen */
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
/** Shown as cancelling (pink), e.g. for a choice that removes something */
|
|
11
|
+
cancelling?: boolean;
|
|
12
|
+
/** The button's style instead (e.g. `dismiss`: pink text without a background) */
|
|
13
|
+
variant?: ButtonVariant;
|
|
14
|
+
}
|
|
15
|
+
/** What `pc-choose` gives when the dialog is dismissed with Escape */
|
|
16
|
+
export declare const CANCEL_CHOICE = "cancel";
|
|
17
|
+
/**
|
|
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).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* html`<pc-dialog heading="Delete layer" message='Delete "Sketch"?'
|
|
26
|
+
* .choices=${[{ value: "delete", label: "Delete", cancelling: true }, { value: "cancel", label: "Cancel" }]}
|
|
27
|
+
* @pc-choose=${(e) => choose(e.detail.value)}></pc-dialog>`
|
|
28
|
+
*/
|
|
29
|
+
export declare class PcDialog extends PcElement {
|
|
30
|
+
static properties: {
|
|
31
|
+
heading: {
|
|
32
|
+
type: StringConstructor;
|
|
33
|
+
};
|
|
34
|
+
message: {
|
|
35
|
+
type: StringConstructor;
|
|
36
|
+
};
|
|
37
|
+
choices: {
|
|
38
|
+
attribute: boolean;
|
|
39
|
+
};
|
|
40
|
+
translations: {
|
|
41
|
+
attribute: boolean;
|
|
42
|
+
};
|
|
43
|
+
icons: {
|
|
44
|
+
attribute: boolean;
|
|
45
|
+
};
|
|
46
|
+
theme: {
|
|
47
|
+
type: StringConstructor;
|
|
48
|
+
reflect: boolean;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
static styles: import("lit").CSSResultGroup[];
|
|
52
|
+
heading: string;
|
|
53
|
+
message: string;
|
|
54
|
+
choices: readonly DialogChoice[];
|
|
55
|
+
constructor();
|
|
56
|
+
firstUpdated(changed: PropertyValues<this>): void;
|
|
57
|
+
/** Choose one of the choices (or `cancel`) */
|
|
58
|
+
choose(value: string): void;
|
|
59
|
+
private readonly onKeyDown;
|
|
60
|
+
connectedCallback(): void;
|
|
61
|
+
disconnectedCallback(): void;
|
|
62
|
+
render(): import("lit").TemplateResult<1>;
|
|
63
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { PcElement, defineElement, emit } from "../../base/index.js";
|
|
2
|
+
import { css, html, nothing } from "lit";
|
|
3
|
+
// src/ui/elements/PcDialog/index.ts
|
|
4
|
+
/** What `pc-choose` gives when the dialog is dismissed with Escape */
|
|
5
|
+
var CANCEL_CHOICE = "cancel";
|
|
6
|
+
/**
|
|
7
|
+
* A modal window asking the user to choose, over a dimmed backdrop, with a heading, a message, any content of its own,
|
|
8
|
+
* and its choices, each on its own row (the cancel choice and those marked `cancelling` in pink, the others purple,
|
|
9
|
+
* unless a choice gives its own `variant`).
|
|
10
|
+
* Focus moves to the first choice when it opens. Fires `pc-choose` with the chosen value (`cancel` for Escape, pressed
|
|
11
|
+
* anywhere while it's open).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* html`<pc-dialog heading="Delete layer" message='Delete "Sketch"?'
|
|
15
|
+
* .choices=${[{ value: "delete", label: "Delete", cancelling: true }, { value: "cancel", label: "Cancel" }]}
|
|
16
|
+
* @pc-choose=${(e) => choose(e.detail.value)}></pc-dialog>`
|
|
17
|
+
*/
|
|
18
|
+
var PcDialog = class extends PcElement {
|
|
19
|
+
static properties = {
|
|
20
|
+
...PcElement.properties,
|
|
21
|
+
heading: { type: String },
|
|
22
|
+
message: { type: String },
|
|
23
|
+
choices: { attribute: false }
|
|
24
|
+
};
|
|
25
|
+
static styles = [PcElement.styles, css`
|
|
26
|
+
:host {
|
|
27
|
+
position: fixed;
|
|
28
|
+
z-index: 20;
|
|
29
|
+
inset: 0;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
padding: var(--pc-space-3);
|
|
34
|
+
background: var(--pc-backdrop);
|
|
35
|
+
}
|
|
36
|
+
[role="dialog"] {
|
|
37
|
+
display: flex;
|
|
38
|
+
width: 100%;
|
|
39
|
+
max-width: 400px;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
gap: var(--pc-space-2);
|
|
42
|
+
padding: var(--pc-space-3);
|
|
43
|
+
border-radius: var(--pc-radius);
|
|
44
|
+
background: var(--pc-surface-raised);
|
|
45
|
+
color: var(--pc-text);
|
|
46
|
+
box-shadow: var(--pc-shadow);
|
|
47
|
+
font-family: var(--pc-font);
|
|
48
|
+
font-size: var(--pc-font-size);
|
|
49
|
+
}
|
|
50
|
+
h2 {
|
|
51
|
+
margin: 0;
|
|
52
|
+
font-size: 1.2em;
|
|
53
|
+
}
|
|
54
|
+
p {
|
|
55
|
+
margin: 0;
|
|
56
|
+
}
|
|
57
|
+
.choices {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: var(--pc-space-2);
|
|
61
|
+
}
|
|
62
|
+
`];
|
|
63
|
+
constructor() {
|
|
64
|
+
super();
|
|
65
|
+
this.heading = "";
|
|
66
|
+
this.message = "";
|
|
67
|
+
this.choices = [];
|
|
68
|
+
}
|
|
69
|
+
firstUpdated(changed) {
|
|
70
|
+
super.firstUpdated(changed);
|
|
71
|
+
const first = this.renderRoot.querySelector("pc-button");
|
|
72
|
+
if (first) first.updateComplete.then(() => first.focus());
|
|
73
|
+
}
|
|
74
|
+
/** Choose one of the choices (or `cancel`) */
|
|
75
|
+
choose(value) {
|
|
76
|
+
emit(this, "pc-choose", { value });
|
|
77
|
+
}
|
|
78
|
+
onKeyDown = (event) => {
|
|
79
|
+
if (event.key !== "Escape") return;
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
this.choose(CANCEL_CHOICE);
|
|
82
|
+
};
|
|
83
|
+
connectedCallback() {
|
|
84
|
+
super.connectedCallback();
|
|
85
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
86
|
+
}
|
|
87
|
+
disconnectedCallback() {
|
|
88
|
+
super.disconnectedCallback();
|
|
89
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
90
|
+
}
|
|
91
|
+
render() {
|
|
92
|
+
return html`<div
|
|
93
|
+
part="dialog"
|
|
94
|
+
role="dialog"
|
|
95
|
+
aria-modal="true"
|
|
96
|
+
aria-labelledby="heading"
|
|
97
|
+
aria-describedby=${this.message ? "message" : nothing}
|
|
98
|
+
>
|
|
99
|
+
<h2 id="heading" part="heading">${this.heading}</h2>
|
|
100
|
+
${this.message ? html`<p id="message" part="message">${this.message}</p>` : nothing}
|
|
101
|
+
<slot></slot>
|
|
102
|
+
<div class="choices" part="choices">
|
|
103
|
+
${this.choices.map((choice) => html`<pc-button
|
|
104
|
+
variant=${choice.variant ?? (choice.value === "cancel" || choice.cancelling ? "cancel" : "accent")}
|
|
105
|
+
data-value=${choice.value}
|
|
106
|
+
@click=${() => this.choose(choice.value)}
|
|
107
|
+
>${choice.label}</pc-button
|
|
108
|
+
>`)}
|
|
109
|
+
</div>
|
|
110
|
+
</div>`;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
defineElement("pc-dialog", PcDialog);
|
|
114
|
+
export { CANCEL_CHOICE, PcDialog };
|