@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.
- package/README.md +30 -15
- 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/presets/round/index.d.ts +1 -1
- package/dist/lib/brushes/presets/round/index.js +12 -7
- package/dist/lib/brushes/presets/square/index.d.ts +1 -1
- package/dist/lib/brushes/presets/square/index.js +7 -3
- package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
- package/dist/lib/brushes/shapeImage/index.js +15 -9
- package/dist/lib/brushes/shapes/index.d.ts +18 -0
- package/dist/lib/brushes/shapes/index.js +86 -0
- package/dist/lib/brushes/types/index.d.ts +8 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +29 -2
- package/dist/lib/classes/BrushSettings/index.js +51 -14
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +31 -2
- package/dist/lib/classes/KeyboardShortcuts/index.js +66 -4
- 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 +72 -3
- package/dist/lib/classes/PaintCanvas/index.js +106 -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 +3 -2
- package/dist/lib/classes/SettingsAutosave/index.js +6 -3
- package/dist/lib/classes/UiSettings/index.d.ts +68 -0
- package/dist/lib/classes/UiSettings/index.js +82 -0
- 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 +7 -2
- package/dist/lib/settings/schema.d.ts +67 -0
- package/dist/lib/settings/schema.js +20 -0
- package/dist/lib/validation/validators.generated.js +13191 -3655
- package/dist/locales/en-GB.js +39 -2
- package/dist/locales/en-US.js +39 -2
- package/dist/locales/fr.js +39 -2
- package/dist/locales/ja.js +39 -2
- package/dist/locales/ko.js +39 -2
- package/dist/locales/zh-Hans.js +39 -2
- package/dist/locales/zh-Hant.js +39 -2
- package/dist/package.js +1 -1
- package/dist/testing/recordingCanvas.js +2 -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 +10 -3
- package/dist/ui/base/index.js +19 -4
- package/dist/ui/dismiss/index.d.ts +21 -0
- package/dist/ui/dismiss/index.js +27 -0
- package/dist/ui/drag/index.d.ts +51 -0
- package/dist/ui/drag/index.js +81 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +17 -12
- package/dist/ui/elements/PaintCanvas/index.js +36 -17
- 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 +34 -16
- package/dist/ui/elements/PcBrushConfig/index.js +263 -153
- package/dist/ui/elements/PcBrushConfig/style.js +1 -1
- package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
- package/dist/ui/elements/PcBrushSize/index.js +66 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
- package/dist/ui/elements/PcBrushTest/index.js +79 -6
- 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 +27 -2
- package/dist/ui/elements/PcCanvas/index.js +63 -13
- package/dist/ui/elements/PcCanvas/style.js +1 -1
- package/dist/ui/elements/PcCanvasMap/style.js +1 -1
- package/dist/ui/elements/PcClipboardTools/index.d.ts +49 -0
- package/dist/ui/elements/PcClipboardTools/index.js +125 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +2 -2
- 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/PcInputSettings/index.d.ts +13 -0
- package/dist/ui/elements/PcInputSettings/index.js +16 -1
- package/dist/ui/elements/PcMenu/index.d.ts +48 -3
- package/dist/ui/elements/PcMenu/index.js +111 -19
- package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
- package/dist/ui/elements/PcNumberSlider/index.js +247 -0
- package/dist/ui/elements/PcPane/index.d.ts +3 -1
- package/dist/ui/elements/PcPane/index.js +44 -7
- package/dist/ui/elements/PcPanel/index.d.ts +2 -2
- package/dist/ui/elements/PcPanel/index.js +8 -8
- package/dist/ui/elements/PcPanelBrushes/index.d.ts +5 -17
- package/dist/ui/elements/PcPanelBrushes/index.js +13 -48
- 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/PcPopover/index.d.ts +39 -1
- package/dist/ui/elements/PcPopover/index.js +72 -4
- 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/PcThemePicker/index.d.ts +17 -15
- package/dist/ui/elements/PcThemePicker/index.js +22 -21
- package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
- package/dist/ui/elements/PcToolSettings/index.js +382 -318
- package/dist/ui/elements/PcToolSettings/style.js +1 -1
- package/dist/ui/elements/PcToolbar/index.d.ts +15 -0
- package/dist/ui/elements/PcToolbar/index.js +55 -14
- package/dist/ui/elements/PcToolbar/style.js +1 -1
- package/dist/ui/icons/images/icon-brush@2x.js +3 -0
- package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
- package/dist/ui/icons/images/icon-cog@2x.js +3 -0
- package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
- package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
- package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
- package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
- package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
- package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
- package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
- package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-layers@2x.js +3 -0
- package/dist/ui/icons/images/icon-linked@2x.js +3 -0
- package/dist/ui/icons/images/icon-locked@2x.js +3 -0
- package/dist/ui/icons/images/icon-moon@2x.js +3 -0
- package/dist/ui/icons/images/icon-pan@2x.js +3 -0
- package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
- package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
- package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
- package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-redo@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
- package/dist/ui/icons/images/icon-star@2x.js +3 -0
- package/dist/ui/icons/images/icon-sun@2x.js +3 -0
- package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
- package/dist/ui/icons/images/icon-undo@2x.js +3 -0
- package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
- package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
- package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-visible@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
- package/dist/ui/icons/index.d.ts +1 -1
- package/dist/ui/icons/index.js +86 -72
- package/dist/ui/index.d.ts +13 -3
- package/dist/ui/index.js +15 -4
- package/dist/ui/layout/index.d.ts +9 -1
- package/dist/ui/layout/index.js +11 -2
- package/dist/ui/styles/index.d.ts +21 -1
- package/dist/ui/styles/index.js +73 -14
- package/dist/ui/tokens/index.d.ts +5 -4
- package/dist/ui/tokens/index.js +12 -7
- package/locales/en-GB.json +39 -2
- package/locales/en-US.json +39 -2
- package/locales/fr.json +39 -2
- package/locales/ja.json +39 -2
- package/locales/ko.json +39 -2
- package/locales/zh-Hans.json +39 -2
- package/locales/zh-Hant.json +39 -2
- package/package.json +27 -18
- package/dist/ui/icons/images/brush.js +0 -3
- package/dist/ui/icons/images/circle-mask.js +0 -3
- package/dist/ui/icons/images/clipboard.js +0 -3
- package/dist/ui/icons/images/cursor.js +0 -3
- package/dist/ui/icons/images/eraser.js +0 -3
- package/dist/ui/icons/images/eyedropper.js +0 -3
- package/dist/ui/icons/images/flip-horizontal.js +0 -3
- package/dist/ui/icons/images/flip-vertical.js +0 -3
- package/dist/ui/icons/images/focus-pen.js +0 -3
- package/dist/ui/icons/images/freehand-mask.js +0 -3
- package/dist/ui/icons/images/hidden.js +0 -3
- package/dist/ui/icons/images/layers.js +0 -3
- package/dist/ui/icons/images/linked.js +0 -3
- package/dist/ui/icons/images/locked.js +0 -3
- package/dist/ui/icons/images/moon.js +0 -3
- package/dist/ui/icons/images/pan.js +0 -3
- package/dist/ui/icons/images/polygon-mask.js +0 -3
- package/dist/ui/icons/images/pressure.js +0 -3
- package/dist/ui/icons/images/randomise.js +0 -3
- package/dist/ui/icons/images/rectangle-mask.js +0 -3
- package/dist/ui/icons/images/redo.js +0 -3
- package/dist/ui/icons/images/rotate-90-left.js +0 -3
- package/dist/ui/icons/images/rotate-90-right.js +0 -3
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
- package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
- package/dist/ui/icons/images/smoothing-linear.js +0 -3
- package/dist/ui/icons/images/star.js +0 -3
- package/dist/ui/icons/images/sun.js +0 -3
- package/dist/ui/icons/images/tilt.js +0 -3
- package/dist/ui/icons/images/undo.js +0 -3
- package/dist/ui/icons/images/unlinked.js +0 -3
- package/dist/ui/icons/images/unlocked.js +0 -3
- package/dist/ui/icons/images/visible.js +0 -3
- package/dist/ui/icons/images/zoom-in.js +0 -3
- package/dist/ui/icons/images/zoom-out.js +0 -3
- package/dist/ui/icons/images/zoom.js +0 -3
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { PcElement, defineElement } from "../../base/index.js";
|
|
2
|
+
import { css, html, nothing } from "lit";
|
|
3
|
+
// src/ui/elements/PcDialogWindow/index.ts
|
|
4
|
+
/** Event fired when the user asks to close a dialog window (detail: `{ reason }`); cancel it to keep the window open */
|
|
5
|
+
var DIALOG_DISMISS_EVENT = "pc-dismiss";
|
|
6
|
+
/** Header and footer slots, from start to end */
|
|
7
|
+
var REGIONS = [
|
|
8
|
+
"start",
|
|
9
|
+
"middle",
|
|
10
|
+
"end"
|
|
11
|
+
];
|
|
12
|
+
/** Reads a boolean attribute that's on unless it's `"false"` (for options that are on by default) */
|
|
13
|
+
var onUnlessFalse = {
|
|
14
|
+
fromAttribute: (value) => value !== null && value !== "false",
|
|
15
|
+
toAttribute: (value) => value ? "" : "false"
|
|
16
|
+
};
|
|
17
|
+
/** How many open modal windows are keeping the page from scrolling */
|
|
18
|
+
var scrollLocks = 0;
|
|
19
|
+
/** The page's own `overflow` while windows keep it from scrolling, to put back */
|
|
20
|
+
var pageOverflow = "";
|
|
21
|
+
/**
|
|
22
|
+
* Keep the page from scrolling (or let it scroll again once every window that kept it from scrolling has closed).
|
|
23
|
+
*/
|
|
24
|
+
var lockPageScroll = (lock) => {
|
|
25
|
+
const root = document.documentElement;
|
|
26
|
+
if (lock && scrollLocks++ === 0) {
|
|
27
|
+
pageOverflow = root.style.overflow;
|
|
28
|
+
root.style.overflow = "hidden";
|
|
29
|
+
} else if (!lock && --scrollLocks === 0) root.style.overflow = pageOverflow;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The element with keyboard focus, inside shadow roots too.
|
|
33
|
+
*/
|
|
34
|
+
var focusedElement = () => {
|
|
35
|
+
let focused = document.activeElement;
|
|
36
|
+
while (focused?.shadowRoot?.activeElement) focused = focused.shadowRoot.activeElement;
|
|
37
|
+
return focused;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Whether a named slot has content (only elements can go in named slots).
|
|
41
|
+
*/
|
|
42
|
+
var hasContent = (slot) => slot.assignedElements({ flatten: true }).length > 0;
|
|
43
|
+
/**
|
|
44
|
+
* A window over the page, built on the browser's `<dialog>`: centred in the viewport, never bigger than it, with a
|
|
45
|
+
* 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
|
|
46
|
+
* in it (moving to its first control, and back to where it was once it closes).
|
|
47
|
+
*
|
|
48
|
+
* Content goes in its slots: `header-start`, `header-middle` (which names the window for screen readers, unless it has a
|
|
49
|
+
* `label`; `description` describes it), `header-end`, `body` (or no slot), `footer-start`, `footer-middle` and `footer-end`. The body scrolls when
|
|
50
|
+
* there's too much to fit, keeping the header and footer in view. The header and footer only show with content (or the
|
|
51
|
+
* ⨉ button).
|
|
52
|
+
*
|
|
53
|
+
* Options:
|
|
54
|
+
* - `closable` (on by default; `closable="false"` turns it off): a ⨉ button at the top right.
|
|
55
|
+
* - `shaded`: shade the page under it.
|
|
56
|
+
* - `dismissible` (on by default; `dismissible="false"` turns it off): a press outside it closes it.
|
|
57
|
+
* - `inline`: cover only its nearest positioned ancestor instead of the whole page, which stays usable (and scrolls).
|
|
58
|
+
*
|
|
59
|
+
* The ⨉ button, a press outside it (while `dismissible`) and Escape (while `closable` or `dismissible`) fire a cancellable
|
|
60
|
+
* `pc-dismiss` event (detail: `{ reason }`, one of `close`, `outside` or `escape`); unless it's cancelled, the window
|
|
61
|
+
* closes (`open` becomes false). `--pc-dialog-width` sets its width (fitted to its content by default).
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* html`<pc-dialog-window open shaded label="Keyboard shortcuts" @pc-dismiss=${close}>
|
|
65
|
+
* <h2 slot="header-middle">Keyboard shortcuts</h2>
|
|
66
|
+
* <pc-shortcut-settings slot="body" .settings=${settings}></pc-shortcut-settings>
|
|
67
|
+
* <pc-button slot="footer-end" @click=${close}>Done</pc-button>
|
|
68
|
+
* </pc-dialog-window>`
|
|
69
|
+
*/
|
|
70
|
+
var PcDialogWindow = class extends PcElement {
|
|
71
|
+
static properties = {
|
|
72
|
+
...PcElement.properties,
|
|
73
|
+
open: {
|
|
74
|
+
type: Boolean,
|
|
75
|
+
reflect: true
|
|
76
|
+
},
|
|
77
|
+
closable: {
|
|
78
|
+
converter: onUnlessFalse,
|
|
79
|
+
reflect: true
|
|
80
|
+
},
|
|
81
|
+
shaded: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
reflect: true
|
|
84
|
+
},
|
|
85
|
+
dismissible: {
|
|
86
|
+
converter: onUnlessFalse,
|
|
87
|
+
reflect: true
|
|
88
|
+
},
|
|
89
|
+
inline: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
reflect: true
|
|
92
|
+
},
|
|
93
|
+
label: { type: String },
|
|
94
|
+
description: { type: String },
|
|
95
|
+
filled: { state: true }
|
|
96
|
+
};
|
|
97
|
+
static styles = [PcElement.styles, css`
|
|
98
|
+
:host {
|
|
99
|
+
display: contents;
|
|
100
|
+
--pc-dialog-gutter: var(--pc-space-3);
|
|
101
|
+
}
|
|
102
|
+
/* Inline: over the nearest positioned ancestor, while open */
|
|
103
|
+
:host([inline]) {
|
|
104
|
+
position: absolute;
|
|
105
|
+
z-index: 20;
|
|
106
|
+
inset: 0;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
box-sizing: border-box;
|
|
111
|
+
padding: var(--pc-dialog-gutter);
|
|
112
|
+
}
|
|
113
|
+
:host([inline]:not([open])) {
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
:host([inline][shaded]) {
|
|
117
|
+
background: var(--pc-backdrop);
|
|
118
|
+
}
|
|
119
|
+
dialog {
|
|
120
|
+
max-width: none;
|
|
121
|
+
max-height: none;
|
|
122
|
+
margin: auto;
|
|
123
|
+
padding: 0;
|
|
124
|
+
border: 0;
|
|
125
|
+
background: transparent;
|
|
126
|
+
color: var(--pc-text);
|
|
127
|
+
overflow: visible;
|
|
128
|
+
}
|
|
129
|
+
dialog[open] {
|
|
130
|
+
display: flex;
|
|
131
|
+
}
|
|
132
|
+
:host([inline]) dialog {
|
|
133
|
+
position: static;
|
|
134
|
+
max-height: 100%;
|
|
135
|
+
margin: 0;
|
|
136
|
+
}
|
|
137
|
+
dialog::backdrop {
|
|
138
|
+
background: transparent;
|
|
139
|
+
}
|
|
140
|
+
:host([shaded]) dialog::backdrop {
|
|
141
|
+
background: var(--pc-backdrop);
|
|
142
|
+
}
|
|
143
|
+
.window {
|
|
144
|
+
display: flex;
|
|
145
|
+
box-sizing: border-box;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
gap: var(--pc-space-2);
|
|
148
|
+
width: var(--pc-dialog-width, auto);
|
|
149
|
+
max-width: calc(100vw - 2 * var(--pc-dialog-gutter));
|
|
150
|
+
/* The visible viewport's height where browsers have it, so phones' toolbars don't cover the footer */
|
|
151
|
+
max-height: calc(100vh - 2 * var(--pc-dialog-gutter));
|
|
152
|
+
max-height: calc(100dvh - 2 * var(--pc-dialog-gutter));
|
|
153
|
+
padding: var(--pc-space-3);
|
|
154
|
+
border-radius: var(--pc-radius);
|
|
155
|
+
background: var(--pc-surface-raised);
|
|
156
|
+
color: var(--pc-text);
|
|
157
|
+
box-shadow: var(--pc-shadow);
|
|
158
|
+
font-family: var(--pc-font);
|
|
159
|
+
font-size: var(--pc-font-size);
|
|
160
|
+
}
|
|
161
|
+
:host([inline]) .window {
|
|
162
|
+
max-height: 100%;
|
|
163
|
+
}
|
|
164
|
+
header,
|
|
165
|
+
footer {
|
|
166
|
+
display: flex;
|
|
167
|
+
flex: none;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: var(--pc-space-2);
|
|
170
|
+
}
|
|
171
|
+
[hidden] {
|
|
172
|
+
display: none !important;
|
|
173
|
+
}
|
|
174
|
+
.start,
|
|
175
|
+
.end {
|
|
176
|
+
display: flex;
|
|
177
|
+
flex: none;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: var(--pc-space-2);
|
|
180
|
+
}
|
|
181
|
+
.middle {
|
|
182
|
+
display: flex;
|
|
183
|
+
flex: 1;
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
gap: var(--pc-space-2);
|
|
186
|
+
min-width: 0;
|
|
187
|
+
}
|
|
188
|
+
.end {
|
|
189
|
+
margin-left: auto;
|
|
190
|
+
}
|
|
191
|
+
/* Scrolls when there's too much to fit; the padding keeps focus outlines in view */
|
|
192
|
+
.body {
|
|
193
|
+
flex: 1 1 auto;
|
|
194
|
+
min-height: 0;
|
|
195
|
+
margin: calc(-1 * var(--pc-space-1));
|
|
196
|
+
padding: var(--pc-space-1);
|
|
197
|
+
overflow: auto;
|
|
198
|
+
overscroll-behavior: contain;
|
|
199
|
+
}
|
|
200
|
+
button.close {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
justify-content: center;
|
|
204
|
+
width: var(--pc-control-size);
|
|
205
|
+
height: var(--pc-control-size);
|
|
206
|
+
padding: 0;
|
|
207
|
+
border: 0;
|
|
208
|
+
border-radius: var(--pc-radius-pill);
|
|
209
|
+
background: transparent;
|
|
210
|
+
color: var(--pc-text);
|
|
211
|
+
font: inherit;
|
|
212
|
+
font-size: 20px;
|
|
213
|
+
line-height: 1;
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
}
|
|
216
|
+
button.close:hover {
|
|
217
|
+
background: var(--pc-surface-hover);
|
|
218
|
+
}
|
|
219
|
+
button.close:focus-visible {
|
|
220
|
+
outline: var(--pc-focus-width) solid var(--pc-focus);
|
|
221
|
+
}
|
|
222
|
+
`];
|
|
223
|
+
/** Whether it's keeping the page from scrolling */
|
|
224
|
+
scrollLocked = false;
|
|
225
|
+
/** What had focus before it opened, to give it back to if it's taken out of the page while open */
|
|
226
|
+
returnFocus = null;
|
|
227
|
+
/** Whether the `<dialog>` was last shown inline (not modal) */
|
|
228
|
+
shownInline = false;
|
|
229
|
+
/** Whether the press being made started outside the window */
|
|
230
|
+
pressedOutside = false;
|
|
231
|
+
constructor() {
|
|
232
|
+
super();
|
|
233
|
+
this.open = false;
|
|
234
|
+
this.closable = true;
|
|
235
|
+
this.shaded = false;
|
|
236
|
+
this.dismissible = true;
|
|
237
|
+
this.inline = false;
|
|
238
|
+
this.filled = /* @__PURE__ */ new Set();
|
|
239
|
+
}
|
|
240
|
+
/** The `<dialog>` it's built on */
|
|
241
|
+
get dialog() {
|
|
242
|
+
return this.renderRoot.querySelector("dialog");
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Ask to close the window, as the user would: fires `pc-dismiss` (detail: `{ reason }`), and closes it unless that's
|
|
246
|
+
* cancelled.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* dialogWindow.requestClose("close");
|
|
250
|
+
*/
|
|
251
|
+
requestClose(reason) {
|
|
252
|
+
const event = new CustomEvent(DIALOG_DISMISS_EVENT, {
|
|
253
|
+
detail: { reason },
|
|
254
|
+
cancelable: true
|
|
255
|
+
});
|
|
256
|
+
if (this.dispatchEvent(event)) this.open = false;
|
|
257
|
+
}
|
|
258
|
+
connectedCallback() {
|
|
259
|
+
super.connectedCallback();
|
|
260
|
+
if (this.hasUpdated) this.syncOpen();
|
|
261
|
+
}
|
|
262
|
+
disconnectedCallback() {
|
|
263
|
+
super.disconnectedCallback();
|
|
264
|
+
const dialog = this.dialog;
|
|
265
|
+
if (dialog?.open) {
|
|
266
|
+
dialog.close();
|
|
267
|
+
if (this.returnFocus?.isConnected) this.returnFocus.focus();
|
|
268
|
+
}
|
|
269
|
+
this.syncScrollLock();
|
|
270
|
+
}
|
|
271
|
+
firstUpdated(changed) {
|
|
272
|
+
super.firstUpdated(changed);
|
|
273
|
+
this.updateRegions();
|
|
274
|
+
}
|
|
275
|
+
updated(changed) {
|
|
276
|
+
super.updated(changed);
|
|
277
|
+
if (changed.has("open") || changed.has("inline")) this.syncOpen();
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Show or close the `<dialog>` to match `open` (modal unless `inline`), keeping the page from scrolling while a modal
|
|
281
|
+
* window is open.
|
|
282
|
+
*/
|
|
283
|
+
syncOpen() {
|
|
284
|
+
const dialog = this.dialog;
|
|
285
|
+
if (dialog.open && (!this.open || this.shownInline !== this.inline)) dialog.close();
|
|
286
|
+
if (this.open && !dialog.open && this.isConnected) {
|
|
287
|
+
this.shownInline = this.inline;
|
|
288
|
+
this.returnFocus = focusedElement();
|
|
289
|
+
if (this.inline) dialog.show();
|
|
290
|
+
else dialog.showModal();
|
|
291
|
+
}
|
|
292
|
+
this.syncScrollLock();
|
|
293
|
+
}
|
|
294
|
+
/** Keep the page from scrolling while a modal window is open in it, and let it scroll again otherwise */
|
|
295
|
+
syncScrollLock() {
|
|
296
|
+
const lock = this.open && !this.inline && this.isConnected;
|
|
297
|
+
if (lock === this.scrollLocked) return;
|
|
298
|
+
this.scrollLocked = lock;
|
|
299
|
+
lockPageScroll(lock);
|
|
300
|
+
}
|
|
301
|
+
/** Note which header and footer slots have content: only those (and the header and footer with them) are shown */
|
|
302
|
+
updateRegions() {
|
|
303
|
+
this.filled = new Set([...this.renderRoot.querySelectorAll("slot[name]")].filter(hasContent).map((slot) => slot.name));
|
|
304
|
+
}
|
|
305
|
+
/** Whether any of a header's or footer's slots have content */
|
|
306
|
+
hasRegion(region) {
|
|
307
|
+
return REGIONS.some((place) => this.filled.has(`${region}-${place}`));
|
|
308
|
+
}
|
|
309
|
+
onKeyDown = (event) => {
|
|
310
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
if (this.closable || this.dismissible) this.requestClose("escape");
|
|
313
|
+
};
|
|
314
|
+
onCancel = (event) => {
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
if (this.closable || this.dismissible) this.requestClose("escape");
|
|
317
|
+
};
|
|
318
|
+
onClose = () => {
|
|
319
|
+
if (this.isConnected && this.open && !this.dialog?.open) this.open = false;
|
|
320
|
+
};
|
|
321
|
+
onPointerDown = (event) => {
|
|
322
|
+
this.pressedOutside = event.target === event.currentTarget;
|
|
323
|
+
};
|
|
324
|
+
onClick = (event) => {
|
|
325
|
+
const outside = this.pressedOutside && event.target === event.currentTarget;
|
|
326
|
+
this.pressedOutside = false;
|
|
327
|
+
if (outside && this.dismissible) this.requestClose("outside");
|
|
328
|
+
};
|
|
329
|
+
/** A header or footer's start, middle and end slots */
|
|
330
|
+
renderRegion(region, end = nothing) {
|
|
331
|
+
return REGIONS.map((place) => html`<div
|
|
332
|
+
class=${place}
|
|
333
|
+
part="${region}-${place}"
|
|
334
|
+
?hidden=${!this.filled.has(`${region}-${place}`) && (place !== "end" || end === nothing)}
|
|
335
|
+
id=${region === "header" && place === "middle" ? "title" : nothing}
|
|
336
|
+
>
|
|
337
|
+
<slot
|
|
338
|
+
name="${region}-${place}"
|
|
339
|
+
@slotchange=${this.updateRegions}
|
|
340
|
+
></slot>
|
|
341
|
+
${place === "end" ? end : nothing}
|
|
342
|
+
</div>`);
|
|
343
|
+
}
|
|
344
|
+
render() {
|
|
345
|
+
const closeButton = this.closable ? html`<button
|
|
346
|
+
type="button"
|
|
347
|
+
class="close"
|
|
348
|
+
part="close"
|
|
349
|
+
aria-label=${this.t("paintCanvas.common.close")}
|
|
350
|
+
title=${this.t("paintCanvas.common.close")}
|
|
351
|
+
@click=${() => this.requestClose("close")}
|
|
352
|
+
>
|
|
353
|
+
⨉
|
|
354
|
+
</button>` : nothing;
|
|
355
|
+
return html`<dialog
|
|
356
|
+
part="dialog"
|
|
357
|
+
aria-label=${this.label || nothing}
|
|
358
|
+
aria-labelledby=${this.label ? nothing : "title"}
|
|
359
|
+
aria-description=${this.description || nothing}
|
|
360
|
+
@keydown=${this.onKeyDown}
|
|
361
|
+
@cancel=${this.onCancel}
|
|
362
|
+
@close=${this.onClose}
|
|
363
|
+
@pointerdown=${this.onPointerDown}
|
|
364
|
+
@click=${this.onClick}
|
|
365
|
+
>
|
|
366
|
+
<div class="window" part="window">
|
|
367
|
+
<header
|
|
368
|
+
part="header"
|
|
369
|
+
?hidden=${!this.hasRegion("header") && !this.closable}
|
|
370
|
+
>
|
|
371
|
+
${this.renderRegion("header", closeButton)}
|
|
372
|
+
</header>
|
|
373
|
+
<div class="body" part="body">
|
|
374
|
+
<slot name="body"></slot>
|
|
375
|
+
<slot></slot>
|
|
376
|
+
</div>
|
|
377
|
+
<footer part="footer" ?hidden=${!this.hasRegion("footer")}>
|
|
378
|
+
${this.renderRegion("footer")}
|
|
379
|
+
</footer>
|
|
380
|
+
</div>
|
|
381
|
+
</dialog>`;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
defineElement("pc-dialog-window", PcDialogWindow);
|
|
385
|
+
export { DIALOG_DISMISS_EVENT, PcDialogWindow };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { nothing } from "lit";
|
|
2
2
|
import { PcElement } from "../../base";
|
|
3
3
|
import { type InputSettingsData } from "../../../lib/classes/InputSettings";
|
|
4
|
+
import type { UiSettingsLike } from "../../../lib/classes/UiSettings";
|
|
4
5
|
/** Settings `pc-input-settings` can change: PaintCanvas's `InputSettings`, or anything with the same data and `update` */
|
|
5
6
|
export interface InputSettingsLike extends EventTarget {
|
|
6
7
|
readonly data: Readonly<InputSettingsData>;
|
|
@@ -12,6 +13,9 @@ export interface InputSettingsLike extends EventTarget {
|
|
|
12
13
|
* and the language. Changes apply straight away. Hide focus pen mode or the language with `focusPen` and `languages`
|
|
13
14
|
* set to false. Fires `pc-language-change` (with the locale) when the user picks a language.
|
|
14
15
|
*
|
|
16
|
+
* With `ui` (e.g. the canvas's `ui`), the picked language is kept there, and used whenever it changes (e.g. when
|
|
17
|
+
* restored), so it's kept with the other app settings.
|
|
18
|
+
*
|
|
15
19
|
* @example
|
|
16
20
|
* html`<pc-input-settings .settings=${canvas.input}></pc-input-settings>`
|
|
17
21
|
*/
|
|
@@ -20,6 +24,9 @@ export declare class PcInputSettings extends PcElement {
|
|
|
20
24
|
settings: {
|
|
21
25
|
attribute: boolean;
|
|
22
26
|
};
|
|
27
|
+
ui: {
|
|
28
|
+
attribute: boolean;
|
|
29
|
+
};
|
|
23
30
|
focusPen: {
|
|
24
31
|
type: BooleanConstructor;
|
|
25
32
|
};
|
|
@@ -46,6 +53,8 @@ export declare class PcInputSettings extends PcElement {
|
|
|
46
53
|
static styles: import("lit").CSSResultGroup[];
|
|
47
54
|
/** The settings shown and changed */
|
|
48
55
|
settings?: InputSettingsLike;
|
|
56
|
+
/** Where the language is kept, if anywhere (e.g. the canvas's `ui`) */
|
|
57
|
+
ui?: UiSettingsLike;
|
|
49
58
|
/** Whether to show focus pen mode (default true) */
|
|
50
59
|
focusPen: boolean;
|
|
51
60
|
/** Whether to show the language (default true) */
|
|
@@ -55,7 +64,11 @@ export declare class PcInputSettings extends PcElement {
|
|
|
55
64
|
/** Name of the zoom modifier key ("Cmd" or "Ctrl", detected by default) */
|
|
56
65
|
modifier: string;
|
|
57
66
|
private changes;
|
|
67
|
+
/** The kept language last used, so it's only switched to once */
|
|
68
|
+
private usedLocale?;
|
|
58
69
|
constructor();
|
|
70
|
+
/** Use the kept language when it changes */
|
|
71
|
+
protected willUpdate(): void;
|
|
59
72
|
/** A checkbox for an on/off setting */
|
|
60
73
|
private toggle;
|
|
61
74
|
/**
|
|
@@ -18,6 +18,9 @@ var ACTION_KEYS = {
|
|
|
18
18
|
* and the language. Changes apply straight away. Hide focus pen mode or the language with `focusPen` and `languages`
|
|
19
19
|
* set to false. Fires `pc-language-change` (with the locale) when the user picks a language.
|
|
20
20
|
*
|
|
21
|
+
* With `ui` (e.g. the canvas's `ui`), the picked language is kept there, and used whenever it changes (e.g. when
|
|
22
|
+
* restored), so it's kept with the other app settings.
|
|
23
|
+
*
|
|
21
24
|
* @example
|
|
22
25
|
* html`<pc-input-settings .settings=${canvas.input}></pc-input-settings>`
|
|
23
26
|
*/
|
|
@@ -25,6 +28,7 @@ var PcInputSettings = class extends PcElement {
|
|
|
25
28
|
static properties = {
|
|
26
29
|
...PcElement.properties,
|
|
27
30
|
settings: { attribute: false },
|
|
31
|
+
ui: { attribute: false },
|
|
28
32
|
focusPen: { type: Boolean },
|
|
29
33
|
languages: { type: Boolean },
|
|
30
34
|
touch: { type: Boolean },
|
|
@@ -67,7 +71,9 @@ var PcInputSettings = class extends PcElement {
|
|
|
67
71
|
outline-offset: 2px;
|
|
68
72
|
}
|
|
69
73
|
`];
|
|
70
|
-
changes = new ChangeController(this, () => [this.settings]);
|
|
74
|
+
changes = new ChangeController(this, () => [this.settings, this.ui]);
|
|
75
|
+
/** The kept language last used, so it's only switched to once */
|
|
76
|
+
usedLocale;
|
|
71
77
|
constructor() {
|
|
72
78
|
super();
|
|
73
79
|
this.focusPen = true;
|
|
@@ -75,6 +81,13 @@ var PcInputSettings = class extends PcElement {
|
|
|
75
81
|
this.touch = isTouchDevice();
|
|
76
82
|
this.modifier = zoomModifierName();
|
|
77
83
|
}
|
|
84
|
+
/** Use the kept language when it changes */
|
|
85
|
+
willUpdate() {
|
|
86
|
+
const locale = this.ui?.data.locale;
|
|
87
|
+
if (locale === void 0 || locale === this.usedLocale) return;
|
|
88
|
+
this.usedLocale = locale;
|
|
89
|
+
if (locale !== this.translations.locale) this.translations.setLocale(locale);
|
|
90
|
+
}
|
|
78
91
|
/** A checkbox for an on/off setting */
|
|
79
92
|
toggle(data, key, label) {
|
|
80
93
|
return html`<label class="toggle">
|
|
@@ -123,6 +136,8 @@ var PcInputSettings = class extends PcElement {
|
|
|
123
136
|
name="language"
|
|
124
137
|
@change=${(e) => {
|
|
125
138
|
const locale = e.target.value;
|
|
139
|
+
this.usedLocale = locale;
|
|
140
|
+
this.ui?.update({ locale });
|
|
126
141
|
translations.setLocale(locale).then(() => emit(this, "pc-language-change", { locale }));
|
|
127
142
|
}}
|
|
128
143
|
>
|
|
@@ -4,8 +4,10 @@ import { type KeyCombo } from "../../../lib/utils/shortcuts";
|
|
|
4
4
|
import "../PcIcon";
|
|
5
5
|
/** Longest shortcut label shown on a menu item (characters); longer ones are left out */
|
|
6
6
|
export declare const MAX_SHORTCUT_LABEL_LENGTH = 3;
|
|
7
|
-
/** An item in a
|
|
7
|
+
/** An item in a menu that can be chosen */
|
|
8
8
|
export interface MenuItem {
|
|
9
|
+
/** Optional; items are the default kind of menu entry */
|
|
10
|
+
type?: "item";
|
|
9
11
|
/** What `pc-select` gives when it's chosen */
|
|
10
12
|
value: string;
|
|
11
13
|
label: string;
|
|
@@ -18,12 +20,49 @@ export interface MenuItem {
|
|
|
18
20
|
icon?: string;
|
|
19
21
|
/** The action's keyboard shortcut, shown on its right as symbols (e.g. ⌘S) when 3 characters or fewer */
|
|
20
22
|
shortcut?: KeyCombo;
|
|
23
|
+
/** Marks the current choice (e.g. the selected tool) with the `selected` class and a purple background */
|
|
24
|
+
selected?: boolean;
|
|
21
25
|
}
|
|
26
|
+
/** A non-interactive label naming the items after it (up to the next label or divider), e.g. "Tools" */
|
|
27
|
+
export interface MenuLabel {
|
|
28
|
+
type: "label";
|
|
29
|
+
label: string;
|
|
30
|
+
}
|
|
31
|
+
/** A non-interactive line between items */
|
|
32
|
+
export interface MenuDivider {
|
|
33
|
+
type: "divider";
|
|
34
|
+
}
|
|
35
|
+
/** Anything a menu can show: an item, a label or a divider */
|
|
36
|
+
export type MenuEntry = MenuItem | MenuLabel | MenuDivider;
|
|
37
|
+
/** A run of items, with the label before them if there is one */
|
|
38
|
+
interface MenuSection {
|
|
39
|
+
label?: string;
|
|
40
|
+
items: MenuItem[];
|
|
41
|
+
/** Whether a divider comes before it */
|
|
42
|
+
divided: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether a menu entry is an item that can be chosen.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* isMenuItem({ value: "grid", label: "Grid" }); // true
|
|
49
|
+
* isMenuItem({ type: "divider" }); // false
|
|
50
|
+
*/
|
|
51
|
+
export declare const isMenuItem: (entry: MenuEntry) => entry is MenuItem;
|
|
52
|
+
/**
|
|
53
|
+
* Split menu entries into sections: a label or divider starts a new one.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* menuSections([{ type: "label", label: "Tools" }, { value: "brush", label: "Brush" }]);
|
|
57
|
+
* // [{ label: "Tools", items: [{ value: "brush", label: "Brush" }], divided: false }]
|
|
58
|
+
*/
|
|
59
|
+
export declare const menuSections: (entries: readonly MenuEntry[]) => MenuSection[];
|
|
22
60
|
/**
|
|
23
61
|
* A list of menu items, e.g. a layer's actions (see `pc-context-menu` for a button that opens one). While `open`, it shows its items at `x`, `y` (in the page's viewport) and takes
|
|
24
62
|
* keyboard focus: arrow keys, Home and End move between items, Enter or Space chooses one, and Escape or Tab closes it.
|
|
25
63
|
* Items can show a checkmark (toggles) or an icon on their left, and their keyboard shortcut on their right; long
|
|
26
|
-
* labels wrap.
|
|
64
|
+
* labels wrap. Non-interactive labels (`{ type: "label", label }`, small bold text naming the items after them) and
|
|
65
|
+
* dividers (`{ type: "divider" }`) can go between items; keyboard focus skips them.
|
|
27
66
|
* Fires `pc-select` with the chosen item's value, and `pc-close` when it should close (after choosing, Escape, Tab, or a
|
|
28
67
|
* press outside it and its `anchor`). The implementer sets `open` to false in response.
|
|
29
68
|
*
|
|
@@ -65,7 +104,8 @@ export declare class PcMenu extends PcElement {
|
|
|
65
104
|
};
|
|
66
105
|
static styles: import("lit").CSSResultGroup[];
|
|
67
106
|
open: boolean;
|
|
68
|
-
|
|
107
|
+
/** Items, labels and dividers, in order */
|
|
108
|
+
items: readonly MenuEntry[];
|
|
69
109
|
/** Names the menu for screen readers */
|
|
70
110
|
label: string;
|
|
71
111
|
x: number;
|
|
@@ -89,5 +129,10 @@ export declare class PcMenu extends PcElement {
|
|
|
89
129
|
private renderStart;
|
|
90
130
|
/** An item's right side: its shortcut's label, if short enough */
|
|
91
131
|
private renderEnd;
|
|
132
|
+
/** An item's button */
|
|
133
|
+
private renderItem;
|
|
134
|
+
/** A section: its divider and label, if any, and its items (grouped under the label for screen readers) */
|
|
135
|
+
private renderSection;
|
|
92
136
|
render(): import("lit").TemplateResult<1>;
|
|
93
137
|
}
|
|
138
|
+
export {};
|