@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
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import style_default from "./style.js";
|
|
2
2
|
import { ICON_CONTROLS_CSS, PILLS_CSS, WINDOWS_CSS } from "../../styles/index.js";
|
|
3
3
|
import { iconLabel } from "../../icons/index.js";
|
|
4
|
+
import { askConfirm, askText, deletePresetQuestion } from "../../ask/index.js";
|
|
4
5
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
5
6
|
import "../PcBrushTest/index.js";
|
|
7
|
+
import "../PcDialogWindow/index.js";
|
|
8
|
+
import "../PcSliderRange/index.js";
|
|
9
|
+
import "../PcProfileEditor/index.js";
|
|
10
|
+
import ProfileGraphs from "../../../lib/classes/ProfileGraphs/index.js";
|
|
11
|
+
import { isLinear } from "../../../lib/profiles/index.js";
|
|
6
12
|
import { SmoothingType } from "../../../lib/brushes/types/index.js";
|
|
7
|
-
import { PARAM_LIMITS, VARIATION_SOURCES, readRange,
|
|
13
|
+
import { PARAM_LIMITS, VARIATION_SOURCES, configKey, readRange, selectSource, writeRange } from "../../../lib/brushes/configRanges/index.js";
|
|
8
14
|
import { SHAPE_ERRORS, ShapeImageError, readClipboardShape, readShapeImage } from "../../../lib/brushes/shapeImage/index.js";
|
|
9
15
|
import { BUILT_IN_SHAPES, builtInShapeId } from "../../../lib/brushes/shapes/index.js";
|
|
10
16
|
import "../../../lib/brushes/StrokeSampler/index.js";
|
|
@@ -36,6 +42,15 @@ var SOURCE_ICONS = {
|
|
|
36
42
|
tilt: "tilt",
|
|
37
43
|
randomise: "randomise"
|
|
38
44
|
};
|
|
45
|
+
/** Translation keys of the varying settings' names */
|
|
46
|
+
var PARAM_LABELS = {
|
|
47
|
+
width: "paintCanvas.common.width",
|
|
48
|
+
height: "paintCanvas.common.height",
|
|
49
|
+
alpha: "paintCanvas.common.opacity",
|
|
50
|
+
rotation: "paintCanvas.brushConfig.rotation",
|
|
51
|
+
offsetX: "paintCanvas.brushConfig.offsetX",
|
|
52
|
+
offsetY: "paintCanvas.brushConfig.offsetY"
|
|
53
|
+
};
|
|
39
54
|
/** Translation keys of the variation sources' names */
|
|
40
55
|
var SOURCE_LABELS = {
|
|
41
56
|
pressure: "paintCanvas.toolSettings.pressure",
|
|
@@ -53,21 +68,17 @@ var SHAPE_ERROR_KEYS = {
|
|
|
53
68
|
};
|
|
54
69
|
/** Largest stamp spacing in pixels */
|
|
55
70
|
var MAX_SPACING = 200;
|
|
56
|
-
/** Read a number input, or undefined if it isn't a number */
|
|
57
|
-
var numberValue = (event) => {
|
|
58
|
-
const value = event.target.valueAsNumber;
|
|
59
|
-
return Number.isFinite(value) ? value : void 0;
|
|
60
|
-
};
|
|
61
71
|
/**
|
|
62
72
|
* The brush (or eraser) configuration window: a testing area, the shape (pressing it picks a built-in shape from the
|
|
63
73
|
* device's own list, which says "Custom" for an uploaded or pasted one; or upload or paste one), smoothing, width, height,
|
|
64
74
|
* opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
|
|
65
|
-
* pressure, tilt and randomness; when any is on, the setting has a min and max
|
|
75
|
+
* pressure, tilt and randomness; when any is on, the setting has a min and max, set with a `<pc-slider-range>`.
|
|
66
76
|
*
|
|
67
77
|
* Changes stay in the window until "Save" (fires `pc-save` with the settings, to apply to the current tool) or "Add to
|
|
68
78
|
* presets" (saves them as one of the user's presets, asking before replacing one with the same name, and selects it).
|
|
69
|
-
* "Delete preset…" shows when editing one of the user's presets.
|
|
70
|
-
*
|
|
79
|
+
* "Delete preset…" shows when editing one of the user's presets. It's a shaded `<pc-dialog-window>`, closed with its ⨉
|
|
80
|
+
* button or Escape (not a press outside it, which would lose the changes). Fires `pc-close` when closed, and `pc-toast`
|
|
81
|
+
* with a message after adding or deleting a preset, or when a shape can't be used.
|
|
71
82
|
*
|
|
72
83
|
* @example
|
|
73
84
|
* html`<pc-brush-config .app=${app} kind="brush" .config=${app.brush.preset} @pc-save=${apply}></pc-brush-config>`
|
|
@@ -80,7 +91,8 @@ var PcBrushConfig = class extends PcElement {
|
|
|
80
91
|
config: { attribute: false },
|
|
81
92
|
editingId: { type: String },
|
|
82
93
|
draft: { state: true },
|
|
83
|
-
busy: { state: true }
|
|
94
|
+
busy: { state: true },
|
|
95
|
+
editingProfile: { state: true }
|
|
84
96
|
};
|
|
85
97
|
static styles = [
|
|
86
98
|
PcElement.styles,
|
|
@@ -89,30 +101,21 @@ var PcBrushConfig = class extends PcElement {
|
|
|
89
101
|
unsafeCSS(ICON_CONTROLS_CSS),
|
|
90
102
|
unsafeCSS(PILLS_CSS)
|
|
91
103
|
];
|
|
92
|
-
confirm
|
|
93
|
-
|
|
104
|
+
/** Asks the user to confirm, in a dialog (replaceable for tests) */
|
|
105
|
+
confirm = (options) => askConfirm(this.renderRoot, options);
|
|
106
|
+
/** Asks the user for text, in a dialog (replaceable for tests) */
|
|
107
|
+
prompt = (options) => askText(this.renderRoot, options);
|
|
94
108
|
/** Turns files into brush shapes (replaceable for tests) */
|
|
95
109
|
readShape = readShapeImage;
|
|
96
110
|
/**
|
|
97
111
|
* Reads a brush shape from the clipboard, or else from image data copied on the canvas (replaceable for tests)
|
|
98
112
|
*/
|
|
99
113
|
readClipboard = () => readClipboardShape(navigator.clipboard, {}, async () => this.app?.copiedPng());
|
|
100
|
-
onKeyDown = (event) => {
|
|
101
|
-
if (event.key === "Escape") this.close();
|
|
102
|
-
};
|
|
103
114
|
constructor() {
|
|
104
115
|
super();
|
|
105
116
|
this.kind = "brush";
|
|
106
117
|
this.busy = false;
|
|
107
118
|
}
|
|
108
|
-
connectedCallback() {
|
|
109
|
-
super.connectedCallback();
|
|
110
|
-
window.addEventListener("keydown", this.onKeyDown);
|
|
111
|
-
}
|
|
112
|
-
disconnectedCallback() {
|
|
113
|
-
super.disconnectedCallback();
|
|
114
|
-
window.removeEventListener("keydown", this.onKeyDown);
|
|
115
|
-
}
|
|
116
119
|
willUpdate(changed) {
|
|
117
120
|
super.willUpdate(changed);
|
|
118
121
|
if (changed.has("config") && this.config) this.draft = structuredClone(this.config);
|
|
@@ -190,10 +193,22 @@ var PcBrushConfig = class extends PcElement {
|
|
|
190
193
|
const app = this.app;
|
|
191
194
|
const draft = this.draft;
|
|
192
195
|
if (!app || !draft) return;
|
|
193
|
-
const name = this.prompt(
|
|
196
|
+
const name = (await this.prompt({
|
|
197
|
+
heading: this.t("paintCanvas.brushConfig.addToPresets"),
|
|
198
|
+
label: this.t("paintCanvas.brushConfig.presetName"),
|
|
199
|
+
value: draft.name ?? "",
|
|
200
|
+
confirm: this.t("paintCanvas.common.save"),
|
|
201
|
+
cancel: this.t("paintCanvas.common.cancel")
|
|
202
|
+
}))?.trim();
|
|
194
203
|
if (!name) return;
|
|
195
204
|
const existing = app.customPresets.findOwn(name, app.authorHandle, app.names.unknownAuthor);
|
|
196
|
-
if (existing && !this.confirm(
|
|
205
|
+
if (existing && !await this.confirm({
|
|
206
|
+
heading: this.t("paintCanvas.presets.replaceHeading"),
|
|
207
|
+
message: this.t("paintCanvas.presets.replaceConfirm", { name }),
|
|
208
|
+
confirm: this.t("paintCanvas.presets.replace"),
|
|
209
|
+
cancel: this.t("paintCanvas.common.cancel"),
|
|
210
|
+
cancelling: true
|
|
211
|
+
})) return;
|
|
197
212
|
const preset = {
|
|
198
213
|
...app.newPreset(draft, name),
|
|
199
214
|
...existing ? { id: existing.id } : {}
|
|
@@ -224,7 +239,7 @@ var PcBrushConfig = class extends PcElement {
|
|
|
224
239
|
const id = this.editingId;
|
|
225
240
|
if (!app || !id) return;
|
|
226
241
|
const name = app.customPresets.get(id)?.name ?? "this preset";
|
|
227
|
-
if (!this.confirm(this.
|
|
242
|
+
if (!await this.confirm(deletePresetQuestion(this.translations, name))) return;
|
|
228
243
|
this.busy = true;
|
|
229
244
|
try {
|
|
230
245
|
await app.customPresets.remove(id);
|
|
@@ -242,32 +257,22 @@ var PcBrushConfig = class extends PcElement {
|
|
|
242
257
|
this.busy = false;
|
|
243
258
|
}
|
|
244
259
|
}
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
?disabled=${disabled || this.busy}
|
|
253
|
-
.value=${String(Math.round(value))}
|
|
254
|
-
@change=${(e) => {
|
|
255
|
-
const next = numberValue(e);
|
|
256
|
-
if (next !== void 0) onChange(next);
|
|
257
|
-
}}
|
|
258
|
-
/>`;
|
|
259
|
-
}
|
|
260
|
-
/** Render a range slider */
|
|
261
|
-
slider(className, value, limits, onChange, disabled = false) {
|
|
262
|
-
return html`<input
|
|
263
|
-
type="range"
|
|
260
|
+
/**
|
|
261
|
+
* Render a slider for one value (a single `<pc-slider-range>`), the same as the ranges of settings that vary.
|
|
262
|
+
*/
|
|
263
|
+
single(className, label, value, limits, onChange, disabled = false) {
|
|
264
|
+
const changed = (event) => onChange(event.detail.value);
|
|
265
|
+
return html`<pc-slider-range
|
|
266
|
+
single
|
|
264
267
|
class=${className}
|
|
268
|
+
label=${label}
|
|
265
269
|
min=${limits.min}
|
|
266
270
|
max=${limits.max}
|
|
271
|
+
.value=${live(Math.round(value))}
|
|
267
272
|
?disabled=${disabled || this.busy}
|
|
268
|
-
|
|
269
|
-
@
|
|
270
|
-
|
|
273
|
+
@pc-input=${changed}
|
|
274
|
+
@pc-change=${changed}
|
|
275
|
+
></pc-slider-range>`;
|
|
271
276
|
}
|
|
272
277
|
/** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
|
|
273
278
|
renderParam(draft, param, label, disabled = false) {
|
|
@@ -275,31 +280,61 @@ var PcBrushConfig = class extends PcElement {
|
|
|
275
280
|
const limits = PARAM_LIMITS[param];
|
|
276
281
|
const isOffset = param === "offsetX" || param === "offsetY";
|
|
277
282
|
const write = (values) => this.changeParam(param, writeRange(draft, param, values));
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
const writeEnds = (event) => write({
|
|
284
|
+
min: event.detail.low,
|
|
285
|
+
max: event.detail.high
|
|
286
|
+
});
|
|
287
|
+
const values = range.varies ? html`<pc-slider-range
|
|
288
|
+
class=${`${param} range`}
|
|
289
|
+
label=${label}
|
|
290
|
+
min=${limits.min}
|
|
291
|
+
max=${limits.max}
|
|
292
|
+
.low=${live(Math.round(range.min))}
|
|
293
|
+
.high=${live(Math.round(range.max))}
|
|
294
|
+
?disabled=${disabled || this.busy}
|
|
295
|
+
@pc-input=${writeEnds}
|
|
296
|
+
@pc-change=${writeEnds}
|
|
297
|
+
></pc-slider-range>` : this.single(param, label, range.value, limits, (value) => write({ value }), disabled || isOffset);
|
|
298
|
+
const active = VARIATION_SOURCES.find((source) => range.sources[source]);
|
|
299
|
+
const select = (source) => this.changeParam(param, selectSource(draft, param, source));
|
|
283
300
|
return html`<div class="row" data-param=${param}>
|
|
284
301
|
<span class="label">${label}</span>
|
|
285
302
|
${values}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
303
|
+
<fieldset class="sources" ?disabled=${disabled || this.busy}>
|
|
304
|
+
<legend class="visually-hidden">
|
|
305
|
+
${this.t("paintCanvas.brushConfig.varyWith", { param: label })}
|
|
306
|
+
</legend>
|
|
307
|
+
${VARIATION_SOURCES.map((source) => html`<label
|
|
308
|
+
class="icon-toggle"
|
|
309
|
+
title=${this.t(SOURCE_LABELS[source])}
|
|
310
|
+
>
|
|
311
|
+
<input
|
|
312
|
+
type="radio"
|
|
313
|
+
name=${`${param}-source`}
|
|
314
|
+
class=${`${param} ${source}`}
|
|
315
|
+
aria-label=${this.t("paintCanvas.brushConfig.variation", {
|
|
294
316
|
param: label,
|
|
295
317
|
source: this.t(SOURCE_LABELS[source])
|
|
296
318
|
})}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
319
|
+
.checked=${live(active === source)}
|
|
320
|
+
@change=${() => select(source)}
|
|
321
|
+
@click=${() => {
|
|
322
|
+
if (active === source) select(void 0);
|
|
323
|
+
}}
|
|
324
|
+
/>
|
|
325
|
+
${iconLabel(this.iconUrl(SOURCE_ICONS[source]), this.t(SOURCE_LABELS[source]))}
|
|
326
|
+
</label>`)}
|
|
327
|
+
</fieldset>
|
|
328
|
+
<button
|
|
329
|
+
type="button"
|
|
330
|
+
class=${`icon-button profile ${param} ${draft[configKey(param)]?.profile ? "configured" : ""}`}
|
|
331
|
+
title=${this.t("paintCanvas.profileEditor.button", { param: label })}
|
|
332
|
+
aria-haspopup="dialog"
|
|
333
|
+
?disabled=${!active || disabled || this.busy}
|
|
334
|
+
@click=${() => this.editingProfile = param}
|
|
335
|
+
>
|
|
336
|
+
${iconLabel(this.iconUrl("profileGraph"), this.t("paintCanvas.profileEditor.button", { param: label }))}
|
|
337
|
+
</button>
|
|
303
338
|
</div>`;
|
|
304
339
|
}
|
|
305
340
|
/**
|
|
@@ -406,13 +441,17 @@ var PcBrushConfig = class extends PcElement {
|
|
|
406
441
|
min: 0,
|
|
407
442
|
max: MAX_SPACING
|
|
408
443
|
};
|
|
409
|
-
return html`<
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
444
|
+
return html`<pc-dialog-window
|
|
445
|
+
open
|
|
446
|
+
shaded
|
|
447
|
+
dismissible="false"
|
|
448
|
+
label=${this.t(this.kind === "eraser" ? "paintCanvas.brushConfig.eraserTitle" : "paintCanvas.brushConfig.brushTitle")}
|
|
449
|
+
@pc-dismiss=${(event) => {
|
|
450
|
+
event.preventDefault();
|
|
451
|
+
this.close();
|
|
452
|
+
}}
|
|
453
|
+
>
|
|
454
|
+
<label slot="header-start" class="name-field">
|
|
416
455
|
${this.t("paintCanvas.brushConfig.name")}
|
|
417
456
|
<input
|
|
418
457
|
class="name"
|
|
@@ -421,86 +460,117 @@ var PcBrushConfig = class extends PcElement {
|
|
|
421
460
|
@input=${(e) => this.change({ name: e.target.value })}
|
|
422
461
|
/>
|
|
423
462
|
</label>
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
${this.
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
${
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
</label>
|
|
476
|
-
<div class="row">
|
|
463
|
+
<div slot="body" class="settings">
|
|
464
|
+
<pc-brush-test
|
|
465
|
+
.config=${draft}
|
|
466
|
+
.tips=${this.app?.tips}
|
|
467
|
+
></pc-brush-test>
|
|
468
|
+
${this.renderShape(draft)}
|
|
469
|
+
${this.renderParam(draft, "width", this.t(PARAM_LABELS.width))}
|
|
470
|
+
<div class="row">
|
|
471
|
+
<span class="label"></span>
|
|
472
|
+
<label
|
|
473
|
+
class="icon-toggle"
|
|
474
|
+
title=${this.t("paintCanvas.toolSettings.constrainProportions")}
|
|
475
|
+
>
|
|
476
|
+
<input
|
|
477
|
+
type="checkbox"
|
|
478
|
+
class="constrain"
|
|
479
|
+
.checked=${constrained}
|
|
480
|
+
?disabled=${this.busy}
|
|
481
|
+
@change=${(e) => this.change({ constrainProportions: e.target.checked })}
|
|
482
|
+
/>
|
|
483
|
+
${iconLabel(this.iconUrl(constrained ? "linked" : "unlinked"), this.t("paintCanvas.toolSettings.constrainProportions"))}
|
|
484
|
+
</label>
|
|
485
|
+
</div>
|
|
486
|
+
${this.renderParam(draft, "height", this.t(PARAM_LABELS.height), constrained)}
|
|
487
|
+
${this.renderParam(draft, "alpha", this.t(PARAM_LABELS.alpha))}
|
|
488
|
+
${this.renderParam(draft, "rotation", this.t(PARAM_LABELS.rotation))}
|
|
489
|
+
${this.renderParam(draft, "offsetX", this.t(PARAM_LABELS.offsetX))}
|
|
490
|
+
${this.renderParam(draft, "offsetY", this.t(PARAM_LABELS.offsetY))}
|
|
491
|
+
<div class="row">
|
|
492
|
+
<span class="label"
|
|
493
|
+
>${this.t("paintCanvas.brushConfig.sampleRate")}</span
|
|
494
|
+
>
|
|
495
|
+
${this.single("sample-rate", this.t("paintCanvas.brushConfig.sampleRate"), draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
|
|
496
|
+
</div>
|
|
497
|
+
<div class="row">
|
|
498
|
+
<span class="label"
|
|
499
|
+
>${this.t("paintCanvas.brushConfig.spacing")}</span
|
|
500
|
+
>
|
|
501
|
+
${this.single("spacing", this.t("paintCanvas.brushConfig.spacing"), draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
|
|
502
|
+
</div>
|
|
503
|
+
<label>
|
|
504
|
+
<input
|
|
505
|
+
type="checkbox"
|
|
506
|
+
class="snap"
|
|
507
|
+
.checked=${draft.snapToPixelGrid ?? false}
|
|
508
|
+
?disabled=${this.busy}
|
|
509
|
+
@change=${(e) => this.change({ snapToPixelGrid: e.target.checked })}
|
|
510
|
+
/>
|
|
511
|
+
${this.t("paintCanvas.brushConfig.snapToPixelGrid")}
|
|
512
|
+
</label>
|
|
513
|
+
</div>
|
|
477
514
|
${this.editingId ? html`<button
|
|
515
|
+
slot="footer-start"
|
|
478
516
|
type="button"
|
|
479
|
-
class="delete"
|
|
517
|
+
class="cancel delete"
|
|
480
518
|
?disabled=${this.busy}
|
|
481
519
|
@click=${() => void this.deletePreset()}
|
|
482
520
|
>
|
|
483
521
|
${this.t("paintCanvas.brushConfig.deletePreset")}
|
|
484
522
|
</button>` : nothing}
|
|
485
|
-
<
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
523
|
+
<div slot="footer-end" class="actions">
|
|
524
|
+
<button
|
|
525
|
+
type="button"
|
|
526
|
+
class="add affirmative"
|
|
527
|
+
?disabled=${this.busy}
|
|
528
|
+
@click=${() => void this.addToPresets()}
|
|
529
|
+
>
|
|
530
|
+
${this.t("paintCanvas.brushConfig.addToPresets")}
|
|
531
|
+
</button>
|
|
532
|
+
<button
|
|
533
|
+
type="button"
|
|
534
|
+
class="save affirmative"
|
|
535
|
+
?disabled=${this.busy}
|
|
536
|
+
@click=${() => this.save()}
|
|
537
|
+
>
|
|
538
|
+
${this.t("paintCanvas.common.save")}
|
|
539
|
+
</button>
|
|
540
|
+
</div>
|
|
541
|
+
</pc-dialog-window>
|
|
542
|
+
${this.renderProfileEditor(draft)}`;
|
|
543
|
+
}
|
|
544
|
+
/** The profile graph editor for the setting being edited, while open */
|
|
545
|
+
renderProfileEditor(draft) {
|
|
546
|
+
const param = this.editingProfile;
|
|
547
|
+
if (!param) return nothing;
|
|
548
|
+
const key = configKey(param);
|
|
549
|
+
const variation = draft[key];
|
|
550
|
+
return html`<pc-profile-editor
|
|
551
|
+
.profiles=${this.app?.profiles ?? this.profiles}
|
|
552
|
+
.points=${variation.profile}
|
|
553
|
+
profile-id=${variation.profileId ?? (variation.profile ? nothing : "linear")}
|
|
554
|
+
label=${this.t(PARAM_LABELS[param])}
|
|
555
|
+
@pc-save=${(event) => {
|
|
556
|
+
event.stopPropagation();
|
|
557
|
+
const { points, profileId } = event.detail;
|
|
558
|
+
const linear = isLinear(points);
|
|
559
|
+
this.changeParam(param, { [key]: {
|
|
560
|
+
...variation,
|
|
561
|
+
profile: linear ? void 0 : points,
|
|
562
|
+
profileId: linear ? void 0 : profileId
|
|
563
|
+
} });
|
|
564
|
+
this.editingProfile = void 0;
|
|
565
|
+
}}
|
|
566
|
+
@pc-close=${(event) => {
|
|
567
|
+
event.stopPropagation();
|
|
568
|
+
this.editingProfile = void 0;
|
|
569
|
+
}}
|
|
570
|
+
></pc-profile-editor>`;
|
|
503
571
|
}
|
|
572
|
+
/** Profile graphs, when there's no canvas to keep them */
|
|
573
|
+
profiles = new ProfileGraphs();
|
|
504
574
|
};
|
|
505
575
|
defineElement("pc-brush-config", PcBrushConfig);
|
|
506
576
|
export { PcBrushConfig, SMOOTHING_CHOICES };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/ui/elements/PcBrushConfig/style.css?inline
|
|
2
|
-
var style_default = ":host {\n
|
|
2
|
+
var style_default = ":host {\n display: contents;\n}\npc-dialog-window {\n --pc-dialog-width: 560px;\n}\n.settings {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.name-field {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.actions {\n display: flex;\n gap: 6px;\n}\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n.row > .label {\n width: 6em;\n}\ninput[type=\"number\"] {\n width: 4.5em;\n}\ninput[type=\"range\"] {\n flex: 1;\n min-width: 60px;\n}\npc-slider-range {\n flex: 1;\n min-width: 200px;\n}\n.shape-preview {\n display: inline-block;\n width: 40px;\n height: 40px;\n background: var(--pc-text);\n border-radius: 50%;\n}\n.shape-preview.image {\n border-radius: 0;\n mask-repeat: no-repeat;\n mask-size: contain;\n mask-position: center;\n}\n/* The shape (preview, Upload and Paste) and the smoothing icons on one row */\n.shape-row {\n flex-wrap: nowrap;\n}\n/* The shape, with an invisible list over it: pressing the shape opens the device's own list of shapes */\n.shape-picker {\n position: relative;\n display: inline-flex;\n flex: none;\n border-radius: var(--pc-radius-small);\n}\n.shape-picker:hover {\n background: var(--pc-surface-hover);\n}\n.shape-picker:focus-within {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.shape-select {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\nfieldset.smoothing {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 0 auto;\n padding: 0;\n border: 0;\n}\nfieldset.smoothing legend {\n float: left;\n margin-right: 4px;\n padding: 0;\n}\n/* Pressure, tilt and randomise: one at a time, side by side */\nfieldset.sources {\n display: flex;\n gap: 4px;\n margin: 0;\n padding: 0;\n border: 0;\n}\n/* A setting with a profile graph: its button is purple */\nbutton.icon-button.profile.configured {\n border-color: var(--pc-accent);\n background: var(--pc-accent);\n}\n";
|
|
3
3
|
export { style_default as default };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import style_default from "./style.js";
|
|
2
|
+
import { PILLS_CSS } from "../../styles/index.js";
|
|
2
3
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
3
4
|
import StrokeEngine from "../../../lib/brushes/StrokeEngine/index.js";
|
|
4
5
|
import Layer, { createOffscreenCanvas } from "../../../lib/classes/Layer/index.js";
|
|
@@ -38,7 +39,11 @@ var PcBrushTest = class extends PcElement {
|
|
|
38
39
|
attribute: "show-points"
|
|
39
40
|
}
|
|
40
41
|
};
|
|
41
|
-
static styles = [
|
|
42
|
+
static styles = [
|
|
43
|
+
PcElement.styles,
|
|
44
|
+
unsafeCSS(PILLS_CSS),
|
|
45
|
+
unsafeCSS(style_default)
|
|
46
|
+
];
|
|
42
47
|
constructor() {
|
|
43
48
|
super();
|
|
44
49
|
this.showPoints = false;
|
|
@@ -185,7 +190,7 @@ var PcBrushTest = class extends PcElement {
|
|
|
185
190
|
@pointercancel=${this.onPointerUp}
|
|
186
191
|
></canvas>
|
|
187
192
|
<div class="actions">
|
|
188
|
-
<button type="button" class="clear" @click=${() => this.clear()}>
|
|
193
|
+
<button type="button" class="pill clear" @click=${() => this.clear()}>
|
|
189
194
|
${this.t("paintCanvas.common.clear")}
|
|
190
195
|
</button>
|
|
191
196
|
<label class="show-points">
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/ui/elements/PcBrushTest/style.css?inline
|
|
2
|
-
var style_default = ":host {\n display: block;\n}\ncanvas {\n display: block;\n width: 100%;\n background: #fff;\n border: 1px solid var(--pc-border);\n touch-action: none;\n}\n/* \"Clear\", then \"Show stroke points\" */\n.actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 12px;\n margin-top: 6px;\n}\n.show-points {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n}\n/* \"Clear\": a red pill */\nbutton.clear {\n
|
|
2
|
+
var style_default = ":host {\n display: block;\n}\ncanvas {\n display: block;\n width: 100%;\n background: #fff;\n border: 1px solid var(--pc-border);\n touch-action: none;\n}\n/* \"Clear\", then \"Show stroke points\" */\n.actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 12px;\n margin-top: 6px;\n}\n.show-points {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n}\n/* \"Clear\": a red pill */\nbutton.pill.clear {\n background: var(--pc-red);\n color: #ffffff;\n}\n";
|
|
3
3
|
export { style_default as default };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { PcElement } from "../../base";
|
|
2
|
+
import { type PillSize } from "../../styles";
|
|
2
3
|
import "../PcIcon";
|
|
3
4
|
/** How a button looks */
|
|
4
5
|
export type ButtonVariant = "accent" | "cancel" | "dismiss" | "pill" | "icon";
|
|
5
6
|
/**
|
|
6
|
-
* A button: affirmative (`accent`, the default), cancelling (`cancel`), a quiet text button (`dismiss`), a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* A button: affirmative (`accent`, the default), cancelling (`cancel`), a quiet text button (`dismiss`), a pill (`pill`,
|
|
8
|
+
* the same as `accent`), or a boxed icon button (`icon`, which needs a `label` for screen readers). Its text goes in its
|
|
9
|
+
* content. All but icon buttons are pills, of the `size` given (`pill` by default; see `PILL_SIZE_STYLES`). Clicks
|
|
10
|
+
* reach listeners on the element; a disabled button sends none.
|
|
9
11
|
*
|
|
10
12
|
* @example
|
|
11
13
|
* html`<pc-button variant="cancel" @click=${close}>Cancel</pc-button>`
|
|
14
|
+
* html`<pc-button size="pill-l">Start</pc-button>`
|
|
12
15
|
* html`<pc-button variant="icon" icon="undo" label="Undo"></pc-button>`
|
|
13
16
|
*/
|
|
14
17
|
export declare class PcButton extends PcElement {
|
|
@@ -17,6 +20,10 @@ export declare class PcButton extends PcElement {
|
|
|
17
20
|
type: StringConstructor;
|
|
18
21
|
reflect: boolean;
|
|
19
22
|
};
|
|
23
|
+
size: {
|
|
24
|
+
type: StringConstructor;
|
|
25
|
+
reflect: boolean;
|
|
26
|
+
};
|
|
20
27
|
icon: {
|
|
21
28
|
type: StringConstructor;
|
|
22
29
|
};
|
|
@@ -43,6 +50,8 @@ export declare class PcButton extends PcElement {
|
|
|
43
50
|
};
|
|
44
51
|
static styles: import("lit").CSSResultGroup[];
|
|
45
52
|
variant: ButtonVariant;
|
|
53
|
+
/** How big the pill is (not for icon buttons) */
|
|
54
|
+
size: PillSize;
|
|
46
55
|
/** Name of an icon to show before the text (or alone, for an icon button) */
|
|
47
56
|
icon?: string;
|
|
48
57
|
/** The button's name for screen readers, when its content doesn't say it (e.g. an icon button) */
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
2
|
+
import { PILL_SIZES, pillSizeDeclarations } from "../../styles/index.js";
|
|
2
3
|
import "../PcIcon/index.js";
|
|
3
|
-
import { css, html, nothing } from "lit";
|
|
4
|
+
import { css, html, nothing, unsafeCSS } from "lit";
|
|
4
5
|
// src/ui/elements/PcButton/index.ts
|
|
6
|
+
/** Each pill size, for the buttons that are pills (all but icon buttons) */
|
|
7
|
+
var SIZE_STYLES = unsafeCSS(PILL_SIZES.map((size) => `:host([size="${size}"]) button { ${pillSizeDeclarations(size)} }`).join("\n"));
|
|
5
8
|
/**
|
|
6
|
-
* A button: affirmative (`accent`, the default), cancelling (`cancel`), a quiet text button (`dismiss`), a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* A button: affirmative (`accent`, the default), cancelling (`cancel`), a quiet text button (`dismiss`), a pill (`pill`,
|
|
10
|
+
* the same as `accent`), or a boxed icon button (`icon`, which needs a `label` for screen readers). Its text goes in its
|
|
11
|
+
* content. All but icon buttons are pills, of the `size` given (`pill` by default; see `PILL_SIZE_STYLES`). Clicks
|
|
12
|
+
* reach listeners on the element; a disabled button sends none.
|
|
9
13
|
*
|
|
10
14
|
* @example
|
|
11
15
|
* html`<pc-button variant="cancel" @click=${close}>Cancel</pc-button>`
|
|
16
|
+
* html`<pc-button size="pill-l">Start</pc-button>`
|
|
12
17
|
* html`<pc-button variant="icon" icon="undo" label="Undo"></pc-button>`
|
|
13
18
|
*/
|
|
14
19
|
var PcButton = class extends PcElement {
|
|
@@ -18,6 +23,10 @@ var PcButton = class extends PcElement {
|
|
|
18
23
|
type: String,
|
|
19
24
|
reflect: true
|
|
20
25
|
},
|
|
26
|
+
size: {
|
|
27
|
+
type: String,
|
|
28
|
+
reflect: true
|
|
29
|
+
},
|
|
21
30
|
icon: { type: String },
|
|
22
31
|
label: { type: String },
|
|
23
32
|
disabled: {
|
|
@@ -38,9 +47,8 @@ var PcButton = class extends PcElement {
|
|
|
38
47
|
flex: 1;
|
|
39
48
|
align-items: center;
|
|
40
49
|
justify-content: center;
|
|
50
|
+
box-sizing: border-box;
|
|
41
51
|
gap: var(--pc-space-1);
|
|
42
|
-
min-height: var(--pc-control-size);
|
|
43
|
-
padding: var(--pc-space-1) var(--pc-space-3);
|
|
44
52
|
border: 0;
|
|
45
53
|
/* A joined button group gives each button its corners */
|
|
46
54
|
border-radius: var(--pc-group-radius, var(--pc-radius-pill));
|
|
@@ -48,10 +56,11 @@ var PcButton = class extends PcElement {
|
|
|
48
56
|
color: var(--pc-accent-text);
|
|
49
57
|
font: inherit;
|
|
50
58
|
font-family: var(--pc-font);
|
|
51
|
-
|
|
59
|
+
${unsafeCSS(pillSizeDeclarations("pill"))}
|
|
52
60
|
font-weight: bold;
|
|
53
61
|
cursor: pointer;
|
|
54
62
|
}
|
|
63
|
+
${SIZE_STYLES}
|
|
55
64
|
button:hover:not(:disabled) {
|
|
56
65
|
filter: brightness(1.15);
|
|
57
66
|
}
|
|
@@ -76,12 +85,10 @@ var PcButton = class extends PcElement {
|
|
|
76
85
|
filter: none;
|
|
77
86
|
text-decoration: underline;
|
|
78
87
|
}
|
|
79
|
-
:host([variant="pill"]) button {
|
|
80
|
-
min-height: 30px;
|
|
81
|
-
}
|
|
82
88
|
:host([variant="icon"]) button {
|
|
83
89
|
min-height: 0;
|
|
84
90
|
padding: 2px;
|
|
91
|
+
line-height: normal;
|
|
85
92
|
border: 1px solid var(--pc-border);
|
|
86
93
|
border-radius: var(--pc-group-radius, var(--pc-radius-small));
|
|
87
94
|
background: var(--pc-surface);
|
|
@@ -101,6 +108,7 @@ var PcButton = class extends PcElement {
|
|
|
101
108
|
constructor() {
|
|
102
109
|
super();
|
|
103
110
|
this.variant = "accent";
|
|
111
|
+
this.size = "pill";
|
|
104
112
|
this.disabled = false;
|
|
105
113
|
}
|
|
106
114
|
/** Move keyboard focus to the button */
|