@pixerate/editor-react 0.69.0 → 0.70.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.
|
@@ -477,8 +477,19 @@ var PaintToolbar = ({ controller, tool, onToolChange, fileName = "painting", cla
|
|
|
477
477
|
|
|
478
478
|
// src/paint-editor/BrushPanel.tsx
|
|
479
479
|
import { useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
480
|
-
import {
|
|
481
|
-
|
|
480
|
+
import {
|
|
481
|
+
BLEND_MENU_GROUPS,
|
|
482
|
+
BRUSH_ROTATIONS,
|
|
483
|
+
BRUSH_SLIDERS,
|
|
484
|
+
brushEngineControls,
|
|
485
|
+
brushLoadColor,
|
|
486
|
+
downloadBytes as downloadBytes2,
|
|
487
|
+
multiColorSliders,
|
|
488
|
+
patchBrushValue,
|
|
489
|
+
patchPath,
|
|
490
|
+
readPath
|
|
491
|
+
} from "@pixerate/editor/paint-editor";
|
|
492
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
482
493
|
var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, className }) => {
|
|
483
494
|
const state = usePaintEditorState(controller);
|
|
484
495
|
const [error, attempt] = useAttempt();
|
|
@@ -492,6 +503,10 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
492
503
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
493
504
|
[controller, brushes, paper, thumbnailWidth, thumbnailHeight]
|
|
494
505
|
);
|
|
506
|
+
const engine = brushEngineControls(brush);
|
|
507
|
+
const mc = brush.behavior.multicolor;
|
|
508
|
+
const carried = brushLoadColor(state.mixerLoad);
|
|
509
|
+
const setColors = (colors) => controller.setBrush({ behavior: { multicolor: { colors } } });
|
|
495
510
|
const slug = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "brush";
|
|
496
511
|
return /* @__PURE__ */ jsxs4("div", { className: `${panel} ${className ?? ""}`, "data-testid": "brush-panel", children: [
|
|
497
512
|
/* @__PURE__ */ jsx4("div", { className: "grid grid-cols-2 gap-1", role: "listbox", "aria-label": "Brushes", children: brushes.map((b) => /* @__PURE__ */ jsxs4(
|
|
@@ -523,6 +538,68 @@ var BrushPanel = ({ controller, thumbnailWidth = 112, thumbnailHeight = 32, clas
|
|
|
523
538
|
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", children: [
|
|
524
539
|
/* @__PURE__ */ jsx4("input", { type: "checkbox", checked: brush.tip.pressureOpacity, onChange: (e) => controller.setBrush({ tip: { pressureOpacity: e.target.checked } }) }),
|
|
525
540
|
" Pressure \u2192 opacity"
|
|
541
|
+
] }),
|
|
542
|
+
brush.engine === "dab" && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
543
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Blend" }),
|
|
544
|
+
/* @__PURE__ */ jsx4("select", { "aria-label": "Brush blend", value: brush.tip.blend, onChange: (e) => controller.setBrush({ tip: { blend: e.target.value } }), className: "rounded bg-slate-800 px-1 py-0.5", children: BLEND_MENU_GROUPS.map((g) => /* @__PURE__ */ jsx4("optgroup", { label: g.label, children: g.modes.map((m) => /* @__PURE__ */ jsx4("option", { value: m.id, title: m.description, children: m.label }, m.id)) }, g.label)) })
|
|
545
|
+
] })
|
|
546
|
+
] }),
|
|
547
|
+
engine && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", "data-testid": "brush-engine", children: [
|
|
548
|
+
/* @__PURE__ */ jsx4("span", { className: "font-medium text-slate-300", children: engine.title }),
|
|
549
|
+
engine.sliders.map((c) => /* @__PURE__ */ jsx4(PaintSlider, { control: c, value: readPath(brush, c.path), onChange: (v) => controller.setBrush(patchPath(brush, c.path, v)) }, c.path)),
|
|
550
|
+
engine.mixingPath && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
551
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Mixing" }),
|
|
552
|
+
/* @__PURE__ */ jsxs4(
|
|
553
|
+
"select",
|
|
554
|
+
{
|
|
555
|
+
"aria-label": "Brush mixing",
|
|
556
|
+
value: engine.mixingPath === "blender.mixing" ? brush.blender.mixing : engine.mixingPath === "smudge.mixing" ? brush.smudge.mixing : brush.behavior.mixing,
|
|
557
|
+
onChange: (e) => controller.setBrush(patchBrushValue(brush, engine.mixingPath, e.target.value)),
|
|
558
|
+
className: "rounded bg-slate-800 px-1 py-0.5",
|
|
559
|
+
children: [
|
|
560
|
+
/* @__PURE__ */ jsx4("option", { value: "pigment", children: "Pigment" }),
|
|
561
|
+
/* @__PURE__ */ jsx4("option", { value: "rgb", children: "RGB" })
|
|
562
|
+
]
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
] }),
|
|
566
|
+
engine.dirty && /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2", title: "Keep the colour the brush picked up for the next stroke (needs Pick-up above 0)", children: [
|
|
567
|
+
/* @__PURE__ */ jsx4("input", { type: "checkbox", "aria-label": "Dirty brush", checked: brush.behavior.dirty, onChange: (e) => controller.setBrush({ behavior: { dirty: e.target.checked } }) }),
|
|
568
|
+
" Dirty brush"
|
|
569
|
+
] }),
|
|
570
|
+
engine.multicolor && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", "data-testid": "multicolor", children: [
|
|
571
|
+
/* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
572
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Multi-colour" }),
|
|
573
|
+
/* @__PURE__ */ jsxs4("select", { "aria-label": "Multi-colour", value: mc.mode, onChange: (e) => controller.setBrush({ behavior: { multicolor: { mode: e.target.value } } }), className: "rounded bg-slate-800 px-1 py-0.5", children: [
|
|
574
|
+
/* @__PURE__ */ jsx4("option", { value: "off", children: "Off" }),
|
|
575
|
+
/* @__PURE__ */ jsx4("option", { value: "tip", children: "Across the tip" }),
|
|
576
|
+
/* @__PURE__ */ jsx4("option", { value: "dab", children: "Per dab" })
|
|
577
|
+
] })
|
|
578
|
+
] }),
|
|
579
|
+
mc.mode !== "off" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
580
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-1", "aria-label": "Multi-colour colours", children: [
|
|
581
|
+
mc.colors.map((col, i) => /* @__PURE__ */ jsxs4("span", { className: "flex items-center", children: [
|
|
582
|
+
/* @__PURE__ */ jsx4("input", { type: "color", "aria-label": `Multi-colour ${i + 1}`, value: col, onChange: (e) => setColors(mc.colors.map((x, j) => j === i ? e.target.value : x)), className: "h-5 w-6 rounded bg-transparent" }),
|
|
583
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": `Remove multi-colour ${i + 1}`, className: "px-0.5 text-slate-500 hover:text-white", onClick: () => setColors(mc.colors.filter((_, j) => j !== i)), children: "\xD7" })
|
|
584
|
+
] }, i)),
|
|
585
|
+
mc.colors.length < 8 && /* @__PURE__ */ jsx4("button", { type: "button", className: button, "aria-label": "Add multi-colour", onClick: () => setColors([...mc.colors.length ? mc.colors : [state.color, state.secondaryColor], state.color].slice(0, 8)), children: "+" }),
|
|
586
|
+
mc.colors.length < 2 && /* @__PURE__ */ jsx4("span", { className: "text-[10px] text-slate-500", children: "Uses the paint and second colours" })
|
|
587
|
+
] }),
|
|
588
|
+
mc.mode === "dab" && /* @__PURE__ */ jsxs4("label", { className: "flex items-center justify-between gap-2", children: [
|
|
589
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Order" }),
|
|
590
|
+
/* @__PURE__ */ jsxs4("select", { "aria-label": "Colour order", value: mc.order, onChange: (e) => controller.setBrush({ behavior: { multicolor: { order: e.target.value } } }), className: "rounded bg-slate-800 px-1 py-0.5", children: [
|
|
591
|
+
/* @__PURE__ */ jsx4("option", { value: "sequence", children: "In order" }),
|
|
592
|
+
/* @__PURE__ */ jsx4("option", { value: "random", children: "Random" })
|
|
593
|
+
] })
|
|
594
|
+
] }),
|
|
595
|
+
multiColorSliders(mc.mode).map((c) => /* @__PURE__ */ jsx4(PaintSlider, { control: c, value: readPath(brush, c.path), onChange: (v) => controller.setBrush(patchPath(brush, c.path, v)) }, c.path))
|
|
596
|
+
] })
|
|
597
|
+
] }),
|
|
598
|
+
engine.load && /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
599
|
+
/* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Carries" }),
|
|
600
|
+
/* @__PURE__ */ jsx4("span", { "aria-label": "Brush load", title: carried ?? "Clean", className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: carried ?? "transparent" } }),
|
|
601
|
+
brush.engine === "mixer" && /* @__PURE__ */ jsx4("button", { type: "button", className: button, onClick: () => controller.loadMixerBrush(), children: "Load" }),
|
|
602
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: button, onClick: () => controller.cleanMixerBrush(brush.engine === "mixer"), children: "Clean" })
|
|
526
603
|
] })
|
|
527
604
|
] }),
|
|
528
605
|
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
@@ -598,7 +675,9 @@ import {
|
|
|
598
675
|
HARMONY_KINDS,
|
|
599
676
|
MIXING_PAD_HEIGHT,
|
|
600
677
|
MIXING_PAD_WIDTH,
|
|
678
|
+
PAD_SAMPLE_SIZES,
|
|
601
679
|
hsvToRgb,
|
|
680
|
+
padSampleRadius,
|
|
602
681
|
hueToWheel,
|
|
603
682
|
parseHexColor,
|
|
604
683
|
renderColorWheel,
|
|
@@ -614,6 +693,9 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
614
693
|
const [wheel, setWheel] = useState5("ryb");
|
|
615
694
|
const [kind, setKind] = useState5("complementary");
|
|
616
695
|
const [padTool, setPadTool] = useState5("paint");
|
|
696
|
+
const [sample, setSample] = useState5(3);
|
|
697
|
+
const [parts, setParts] = useState5([1, 1]);
|
|
698
|
+
const [padName, setPadName] = useState5("");
|
|
617
699
|
const padPoints = useRef5(null);
|
|
618
700
|
const ring = useMemo5(() => renderColorWheel(wheelSize, wheel), [wheelSize, wheel]);
|
|
619
701
|
const color = state.color;
|
|
@@ -725,7 +807,7 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
725
807
|
onPointerDown: (e) => {
|
|
726
808
|
const p = padPoint(e);
|
|
727
809
|
if (padTool === "pick") {
|
|
728
|
-
attempt(() => controller.mixingPadPick(p.x, p.y, { setColor: true }));
|
|
810
|
+
attempt(() => controller.mixingPadPick(p.x, p.y, { setColor: true, radius: padSampleRadius(sample) }));
|
|
729
811
|
return;
|
|
730
812
|
}
|
|
731
813
|
padPoints.current = [p];
|
|
@@ -739,7 +821,44 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
739
821
|
},
|
|
740
822
|
children: /* @__PURE__ */ jsx6(PixelCanvas, { pixels: pad, label: "Mixing pad pixels", className: "w-full rounded", style: { aspectRatio: `${MIXING_PAD_WIDTH} / ${MIXING_PAD_HEIGHT}` } })
|
|
741
823
|
}
|
|
742
|
-
)
|
|
824
|
+
),
|
|
825
|
+
/* @__PURE__ */ jsx6("div", { className: "flex flex-wrap items-center gap-1", children: /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1", children: [
|
|
826
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-400", children: "Sample" }),
|
|
827
|
+
/* @__PURE__ */ jsx6("select", { "aria-label": "Pick sample size", value: sample, onChange: (e) => setSample(Number(e.target.value)), className: "rounded bg-slate-800 px-1 py-0.5", children: PAD_SAMPLE_SIZES.map((n) => /* @__PURE__ */ jsx6("option", { value: n, children: n === 1 ? "1 px" : `${n}\xD7${n}` }, n)) })
|
|
828
|
+
] }) }),
|
|
829
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
830
|
+
/* @__PURE__ */ jsx6("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: color } }),
|
|
831
|
+
/* @__PURE__ */ jsx6("input", { type: "number", "aria-label": "Paint colour parts", min: 0, max: 20, value: parts[0], onChange: (e) => setParts([Number(e.target.value), parts[1]]), className: "w-10 rounded bg-slate-800 px-1 py-0.5" }),
|
|
832
|
+
/* @__PURE__ */ jsx6("span", { className: "text-slate-500", children: ":" }),
|
|
833
|
+
/* @__PURE__ */ jsx6("input", { type: "number", "aria-label": "Second colour parts", min: 0, max: 20, value: parts[1], onChange: (e) => setParts([parts[0], Number(e.target.value)]), className: "w-10 rounded bg-slate-800 px-1 py-0.5" }),
|
|
834
|
+
/* @__PURE__ */ jsx6("span", { className: "h-4 w-4 rounded ring-1 ring-slate-700", style: { background: state.secondaryColor } }),
|
|
835
|
+
/* @__PURE__ */ jsx6(
|
|
836
|
+
"button",
|
|
837
|
+
{
|
|
838
|
+
type: "button",
|
|
839
|
+
className: button,
|
|
840
|
+
title: "Mix the paint and second colours in these parts on the pad, and use the mix",
|
|
841
|
+
onClick: () => attempt(
|
|
842
|
+
() => controller.mixOnPad(
|
|
843
|
+
[
|
|
844
|
+
{ color, parts: parts[0] },
|
|
845
|
+
{ color: state.secondaryColor, parts: parts[1] }
|
|
846
|
+
],
|
|
847
|
+
{ setColor: true }
|
|
848
|
+
)
|
|
849
|
+
),
|
|
850
|
+
children: "Mix"
|
|
851
|
+
}
|
|
852
|
+
)
|
|
853
|
+
] }),
|
|
854
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
855
|
+
/* @__PURE__ */ jsx6("input", { value: padName, placeholder: "Pad name", "aria-label": "Pad name", onChange: (e) => setPadName(e.target.value), className: "w-24 rounded bg-slate-800 px-1.5 py-0.5" }),
|
|
856
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: button, onClick: () => attempt(() => (controller.saveMixingPad(padName || void 0), setPadName(""))), children: "Keep pad" })
|
|
857
|
+
] }),
|
|
858
|
+
state.savedMixingPads.length > 0 && /* @__PURE__ */ jsx6("ul", { className: "flex flex-col gap-0.5", "aria-label": "Kept pads", children: state.savedMixingPads.map((p) => /* @__PURE__ */ jsxs6("li", { className: "flex items-center gap-1", children: [
|
|
859
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: `${button} flex-1 text-left`, onClick: () => attempt(() => controller.loadMixingPad(p.id)), children: p.name }),
|
|
860
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: button, "aria-label": `Remove ${p.name}`, onClick: () => attempt(() => controller.removeMixingPad(p.id)), children: "\xD7" })
|
|
861
|
+
] }, p.id)) })
|
|
743
862
|
] }),
|
|
744
863
|
/* @__PURE__ */ jsx6(ErrorLine, { error })
|
|
745
864
|
] });
|
|
@@ -747,7 +866,7 @@ var ColorPanel = ({ controller, wheelSize = 168, mixingPad = true, className })
|
|
|
747
866
|
|
|
748
867
|
// src/paint-editor/PaintLayersPanel.tsx
|
|
749
868
|
import { useState as useState6 } from "react";
|
|
750
|
-
import { BLEND_MENU_GROUPS } from "@pixerate/editor/paint-editor";
|
|
869
|
+
import { BLEND_MENU_GROUPS as BLEND_MENU_GROUPS2 } from "@pixerate/editor/paint-editor";
|
|
751
870
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
752
871
|
function siblingIndex(layers, l) {
|
|
753
872
|
const sibs = layers.filter((x) => x.parentId === l.parentId).reverse();
|
|
@@ -844,7 +963,7 @@ var PaintLayersPanel = ({ controller, className }) => {
|
|
|
844
963
|
/* @__PURE__ */ jsx7("span", { className: "w-14 text-slate-400", children: "Blend" }),
|
|
845
964
|
/* @__PURE__ */ jsxs7("select", { "aria-label": "Layer blend", value: active.blend, onChange: (e) => attempt(() => controller.updateLayer(active.id, { blend: e.target.value })), className: "flex-1 rounded bg-slate-800 px-1 py-0.5", children: [
|
|
846
965
|
active.type === "group" && /* @__PURE__ */ jsx7("option", { value: "pass-through", children: "Pass through" }),
|
|
847
|
-
|
|
966
|
+
BLEND_MENU_GROUPS2.map((g) => /* @__PURE__ */ jsx7("optgroup", { label: g.label, children: g.modes.map((m) => /* @__PURE__ */ jsx7("option", { value: m.id, title: m.description, children: m.label }, m.id)) }, g.label))
|
|
848
967
|
] })
|
|
849
968
|
] }),
|
|
850
969
|
/* @__PURE__ */ jsxs7("div", { className: "flex gap-1", children: [
|