@lvl99dotcom/paintcanvas 0.5.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.
- package/dist/lib/brushes/configRanges/index.d.ts +17 -0
- package/dist/lib/brushes/configRanges/index.js +31 -1
- package/dist/lib/brushes/params/index.d.ts +2 -1
- package/dist/lib/brushes/params/index.js +7 -4
- package/dist/lib/brushes/types/index.d.ts +8 -0
- package/dist/lib/classes/Layer/index.d.ts +10 -0
- package/dist/lib/classes/Layer/index.js +15 -1
- package/dist/lib/classes/LayerAutosave/index.d.ts +9 -15
- package/dist/lib/classes/LayerAutosave/index.js +30 -43
- package/dist/lib/classes/LayerStack/index.d.ts +15 -0
- package/dist/lib/classes/LayerStack/index.js +26 -2
- package/dist/lib/classes/PaintCanvas/index.d.ts +26 -3
- package/dist/lib/classes/PaintCanvas/index.js +39 -12
- package/dist/lib/classes/PngEncoder/index.d.ts +41 -0
- package/dist/lib/classes/PngEncoder/index.js +104 -0
- package/dist/lib/classes/ProfileGraphs/index.d.ts +87 -0
- package/dist/lib/classes/ProfileGraphs/index.js +148 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +1 -1
- package/dist/lib/classes/SettingsAutosave/index.js +2 -1
- package/dist/lib/messages/schema.d.ts +720 -0
- package/dist/lib/presets/schema.d.ts +360 -0
- package/dist/lib/presets/schema.js +8 -1
- package/dist/lib/profiles/index.d.ts +61 -0
- package/dist/lib/profiles/index.js +131 -0
- package/dist/lib/profiles/schema.d.ts +73 -0
- package/dist/lib/profiles/schema.js +53 -0
- package/dist/lib/rendering/sample/index.d.ts +11 -0
- package/dist/lib/rendering/sample/index.js +36 -5
- package/dist/lib/settings/index.d.ts +2 -1
- package/dist/lib/settings/index.js +4 -1
- package/dist/lib/settings/schema.d.ts +49 -0
- package/dist/lib/settings/schema.js +6 -0
- package/dist/lib/validation/validators.generated.js +13064 -3696
- package/dist/locales/en-GB.js +30 -0
- package/dist/locales/en-US.js +30 -0
- package/dist/locales/fr.js +30 -0
- package/dist/locales/ja.js +30 -0
- package/dist/locales/ko.js +30 -0
- package/dist/locales/zh-Hans.js +30 -0
- package/dist/locales/zh-Hant.js +30 -0
- package/dist/package.js +1 -1
- package/dist/ui/ask/index.d.ts +48 -0
- package/dist/ui/ask/index.js +67 -0
- package/dist/ui/base/index.d.ts +8 -1
- package/dist/ui/base/index.js +19 -4
- package/dist/ui/elements/PaintCanvas/index.js +3 -1
- package/dist/ui/elements/PaintCanvas/style.js +1 -1
- package/dist/ui/elements/PcAppSettings/index.d.ts +6 -6
- package/dist/ui/elements/PcAppSettings/index.js +21 -65
- package/dist/ui/elements/PcBrushConfig/index.d.ts +25 -14
- package/dist/ui/elements/PcBrushConfig/index.js +216 -146
- package/dist/ui/elements/PcBrushConfig/style.js +1 -1
- package/dist/ui/elements/PcBrushTest/index.js +7 -2
- package/dist/ui/elements/PcBrushTest/style.js +1 -1
- package/dist/ui/elements/PcButton/index.d.ts +12 -3
- package/dist/ui/elements/PcButton/index.js +18 -10
- package/dist/ui/elements/PcCanvas/index.d.ts +6 -1
- package/dist/ui/elements/PcCanvas/index.js +10 -3
- package/dist/ui/elements/PcCanvas/style.js +1 -1
- package/dist/ui/elements/PcClipboardTools/index.d.ts +5 -4
- package/dist/ui/elements/PcClipboardTools/index.js +12 -5
- package/dist/ui/elements/PcDialog/index.d.ts +46 -10
- package/dist/ui/elements/PcDialog/index.js +102 -51
- package/dist/ui/elements/PcDialogWindow/index.d.ts +141 -0
- package/dist/ui/elements/PcDialogWindow/index.js +385 -0
- package/dist/ui/elements/PcPanelBrushes/index.d.ts +3 -2
- package/dist/ui/elements/PcPanelBrushes/index.js +7 -5
- package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
- package/dist/ui/elements/PcPanelColours/index.js +2 -2
- package/dist/ui/elements/PcPanelColours/style.js +1 -1
- package/dist/ui/elements/PcPanelLayers/index.js +1 -1
- package/dist/ui/elements/PcPanelLayers/style.js +1 -1
- package/dist/ui/elements/PcProfileEditor/index.d.ts +103 -0
- package/dist/ui/elements/PcProfileEditor/index.js +259 -0
- package/dist/ui/elements/PcProfileGraph/index.d.ts +84 -0
- package/dist/ui/elements/PcProfileGraph/index.js +317 -0
- package/dist/ui/elements/PcShortcutSettings/index.js +9 -13
- package/dist/ui/elements/PcSliderRange/index.d.ts +147 -0
- package/dist/ui/elements/PcSliderRange/index.js +307 -0
- package/dist/ui/elements/PcStats/index.d.ts +19 -4
- package/dist/ui/elements/PcStats/index.js +29 -13
- package/dist/ui/elements/PcToolSettings/index.js +6 -6
- package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
- package/dist/ui/icons/index.d.ts +1 -1
- package/dist/ui/icons/index.js +2 -0
- package/dist/ui/index.d.ts +6 -1
- package/dist/ui/index.js +6 -1
- package/dist/ui/styles/index.d.ts +21 -1
- package/dist/ui/styles/index.js +73 -14
- package/locales/en-GB.json +30 -0
- package/locales/en-US.json +30 -0
- package/locales/fr.json +30 -0
- package/locales/ja.json +30 -0
- package/locales/ko.json +30 -0
- package/locales/zh-Hans.json +30 -0
- package/locales/zh-Hant.json +30 -0
- package/package.json +25 -23
|
@@ -15,6 +15,8 @@ export declare const HANDLE_GRAB_DISTANCE = 8;
|
|
|
15
15
|
export declare const ANTS_INTERVAL = 100;
|
|
16
16
|
/** In focus pen mode, how long after a pen lifts touches are still ignored (milliseconds) */
|
|
17
17
|
export declare const PEN_LIFT_GRACE_MS = 500;
|
|
18
|
+
/** The most device pixels per CSS pixel the canvases on screen use (see `PcCanvas.pixelRatio`) */
|
|
19
|
+
export declare const MAX_PIXEL_RATIO = 2;
|
|
18
20
|
/**
|
|
19
21
|
* Space covered by the toolbar and chat on a screen of the given width.
|
|
20
22
|
*
|
|
@@ -92,7 +94,10 @@ export declare class PcCanvas extends PcElement {
|
|
|
92
94
|
insets?: Insets;
|
|
93
95
|
/** The insets in effect (see `insets`) */
|
|
94
96
|
private get activeInsets();
|
|
95
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Device pixels per CSS pixel for the canvases on screen, at most `MAX_PIXEL_RATIO`: phones with 3 have 2.25 times
|
|
99
|
+
* the pixels to draw and put on screen for little visible difference (replaceable for tests).
|
|
100
|
+
*/
|
|
96
101
|
pixelRatio: () => number;
|
|
97
102
|
private changes;
|
|
98
103
|
/** Whether anything but the stroke in progress changed since the last update (so everything is redrawn) */
|
|
@@ -39,6 +39,8 @@ var HANDLE_GRAB_DISTANCE = 8;
|
|
|
39
39
|
var ANTS_INTERVAL = 100;
|
|
40
40
|
/** In focus pen mode, how long after a pen lifts touches are still ignored (milliseconds) */
|
|
41
41
|
var PEN_LIFT_GRACE_MS = 500;
|
|
42
|
+
/** The most device pixels per CSS pixel the canvases on screen use (see `PcCanvas.pixelRatio`) */
|
|
43
|
+
var MAX_PIXEL_RATIO = 2;
|
|
42
44
|
/**
|
|
43
45
|
* Space covered by the toolbar and chat on a screen of the given width.
|
|
44
46
|
*
|
|
@@ -131,8 +133,11 @@ var PcCanvas = class extends PcElement {
|
|
|
131
133
|
const app = this.app;
|
|
132
134
|
return this.insets ?? interfaceInsets(app.viewport.width, app.interfaceVisible);
|
|
133
135
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Device pixels per CSS pixel for the canvases on screen, at most `MAX_PIXEL_RATIO`: phones with 3 have 2.25 times
|
|
138
|
+
* the pixels to draw and put on screen for little visible difference (replaceable for tests).
|
|
139
|
+
*/
|
|
140
|
+
pixelRatio = () => Math.min(window.devicePixelRatio || 1, 2);
|
|
136
141
|
changes = new ChangeController(this, () => [
|
|
137
142
|
this.app,
|
|
138
143
|
this.app?.brush,
|
|
@@ -242,6 +247,7 @@ var PcCanvas = class extends PcElement {
|
|
|
242
247
|
this.frame = 0;
|
|
243
248
|
}
|
|
244
249
|
firstUpdated() {
|
|
250
|
+
this.canvas?.getContext("2d", { willReadFrequently: true });
|
|
245
251
|
if (typeof ResizeObserver !== "undefined") {
|
|
246
252
|
this.resizeObserver = new ResizeObserver(() => this.syncSize());
|
|
247
253
|
this.resizeObserver.observe(this);
|
|
@@ -884,6 +890,7 @@ var PcCanvas = class extends PcElement {
|
|
|
884
890
|
if (!ctx) return;
|
|
885
891
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
886
892
|
const sampling = this.sampling;
|
|
893
|
+
this.sampler.hidden = !sampling;
|
|
887
894
|
if (!sampling) return;
|
|
888
895
|
const ratio = this.pixelRatio();
|
|
889
896
|
ctx.save();
|
|
@@ -1046,4 +1053,4 @@ var PcCanvas = class extends PcElement {
|
|
|
1046
1053
|
}
|
|
1047
1054
|
};
|
|
1048
1055
|
defineElement("pc-canvas", PcCanvas);
|
|
1049
|
-
export { ANTS_INTERVAL, CHAT_WIDTH_RATIO, HANDLE_GRAB_DISTANCE, LASSO_TOOLS, PEN_LIFT_GRACE_MS, PcCanvas, SAMPLER_HEIGHT, SAMPLER_WIDTH, TOOLBAR_WIDTH, TOUCH_SAMPLER_GAP, cursorFor, interfaceInsets, samplerPosition };
|
|
1056
|
+
export { ANTS_INTERVAL, CHAT_WIDTH_RATIO, HANDLE_GRAB_DISTANCE, LASSO_TOOLS, MAX_PIXEL_RATIO, PEN_LIFT_GRACE_MS, PcCanvas, SAMPLER_HEIGHT, SAMPLER_WIDTH, TOOLBAR_WIDTH, TOUCH_SAMPLER_GAP, cursorFor, interfaceInsets, samplerPosition };
|
|
@@ -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 };
|
|
@@ -5,9 +5,10 @@ import "../PcContextMenu";
|
|
|
5
5
|
/** The tools that cut, copy and paste are shown for: the brush, eraser, lassos and Move tool */
|
|
6
6
|
export declare const CLIPBOARD_TOOLS: readonly Tool[];
|
|
7
7
|
/**
|
|
8
|
-
* Cut, copy and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and
|
|
9
|
-
* (see `CLIPBOARD_TOOLS`).
|
|
10
|
-
*
|
|
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.
|
|
11
12
|
*
|
|
12
13
|
* `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
|
|
13
14
|
*
|
|
@@ -40,7 +41,7 @@ export declare class PcClipboardTools extends PcElement {
|
|
|
40
41
|
narrow: boolean;
|
|
41
42
|
private changes;
|
|
42
43
|
constructor();
|
|
43
|
-
/** Cut, copy and paste, each with whether it can be done now */
|
|
44
|
+
/** Cut, copy, copy merged and paste, each with whether it can be done now */
|
|
44
45
|
private actions;
|
|
45
46
|
/** Hide for tools without clipboard actions (e.g. zoom) */
|
|
46
47
|
protected willUpdate(): void;
|
|
@@ -17,9 +17,10 @@ var CLIPBOARD_TOOLS = [
|
|
|
17
17
|
Tool.Move
|
|
18
18
|
];
|
|
19
19
|
/**
|
|
20
|
-
* Cut, copy and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and
|
|
21
|
-
* (see `CLIPBOARD_TOOLS`).
|
|
22
|
-
*
|
|
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.
|
|
23
24
|
*
|
|
24
25
|
* `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
|
|
25
26
|
*
|
|
@@ -62,7 +63,7 @@ var PcClipboardTools = class extends PcElement {
|
|
|
62
63
|
super();
|
|
63
64
|
this.narrow = false;
|
|
64
65
|
}
|
|
65
|
-
/** Cut, copy and paste, each with whether it can be done now */
|
|
66
|
+
/** Cut, copy, copy merged and paste, each with whether it can be done now */
|
|
66
67
|
actions(app) {
|
|
67
68
|
const noCopy = !app.selection.active || app.transform.pending;
|
|
68
69
|
return [
|
|
@@ -78,6 +79,12 @@ var PcClipboardTools = class extends PcElement {
|
|
|
78
79
|
disabled: noCopy,
|
|
79
80
|
run: () => app.copy()
|
|
80
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: "copy-merged",
|
|
84
|
+
label: this.t("paintCanvas.common.copyMerged"),
|
|
85
|
+
disabled: noCopy,
|
|
86
|
+
run: () => app.copyMerged()
|
|
87
|
+
},
|
|
81
88
|
{
|
|
82
89
|
name: "paste",
|
|
83
90
|
label: this.t("paintCanvas.common.paste"),
|
|
@@ -106,7 +113,7 @@ var PcClipboardTools = class extends PcElement {
|
|
|
106
113
|
></pc-context-menu>`;
|
|
107
114
|
return actions.map((action) => html`<button
|
|
108
115
|
type="button"
|
|
109
|
-
class="pill ${action.name}"
|
|
116
|
+
class="pill-s ${action.name}"
|
|
110
117
|
?disabled=${action.disabled}
|
|
111
118
|
@click=${action.run}
|
|
112
119
|
>
|
|
@@ -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
|
-
/**
|
|
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
|
|
19
|
-
*
|
|
20
|
-
* unless a choice gives its own `variant`).
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
60
|
-
|
|
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
|
|
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
|
|
9
|
-
*
|
|
10
|
-
* unless a choice gives its own `variant`).
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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)
|
|
104
|
+
if (!first) return;
|
|
105
|
+
await first.updateComplete;
|
|
106
|
+
first.focus();
|
|
74
107
|
}
|
|
75
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
if (event.key !== "
|
|
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
|
-
|
|
130
|
+
const field = this.field;
|
|
131
|
+
return html`<pc-dialog-window
|
|
94
132
|
part="dialog"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
</
|
|
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
|
+
}
|