|
8 | 8 | } from "@dreamlab/engine"; |
9 | 9 | import { element as elem } from "@dreamlab/ui"; |
10 | 10 | import * as PIXI from "@dreamlab/vendor/pixi.ts"; |
11 | | -import "npm:vanilla-colorful/hex-color-picker.js"; |
| 11 | +import "npm:vanilla-colorful@^0.7.2/hex-color-picker.js"; |
12 | 12 | import { EditorFacadeTilemap } from "../../common/facades/tilemap.ts"; |
13 | | -import { icon, Pipette, SquarePen, X } from "../_icons.tsx"; |
| 13 | +import { icon, Pencil, Pipette, SquarePen, X } from "../_icons.tsx"; |
14 | 14 | import { IconButton } from "../components/icon-button.ts"; |
15 | 15 | import { InspectorUI } from "./inspector.ts"; |
16 | 16 |
|
@@ -207,6 +207,14 @@ export class TileMapViewer { |
207 | 207 | } |
208 | 208 | }); |
209 | 209 | this.#listeners.push(() => onTilemapUpdate.unsubscribe()); |
| 210 | + |
| 211 | + const onColorNamesChanged = () => { |
| 212 | + this.#renderColorHistory(); |
| 213 | + }; |
| 214 | + |
| 215 | + const colorNames = tilemap.values.get("colorNames"); |
| 216 | + colorNames?.onChanged(onColorNamesChanged); |
| 217 | + this.#listeners.push(() => colorNames?.removeChangeListener(onColorNamesChanged)); |
210 | 218 | }); |
211 | 219 |
|
212 | 220 | app.canvas.addEventListener( |
@@ -865,20 +873,88 @@ export class TileMapViewer { |
865 | 873 |
|
866 | 874 | for (const color of colors) { |
867 | 875 | const hexColor = "#" + color.toString(16).padStart(6, "0"); |
868 | | - const swatch = elem("div", { |
869 | | - className: "color-swatch", |
| 876 | + const name = this.#tilemap?.colorNames?.[color]; |
| 877 | + |
| 878 | + const labelSpan = elem("span", { dataset: name ? undefined : { unnamed: "" } }, [ |
| 879 | + name ?? hexColor, |
| 880 | + ]); |
| 881 | + |
| 882 | + const renameButton = elem("button", { title: "Rename" }, [icon(Pencil)]); |
| 883 | + |
| 884 | + const colorButton = elem("button", { |
| 885 | + type: "button", |
| 886 | + className: "color", |
870 | 887 | title: hexColor, |
871 | | - }) as HTMLDivElement; |
872 | | - swatch.style.backgroundColor = hexColor; |
| 888 | + }); |
873 | 889 |
|
874 | | - if (color === this.#selectedColor) { |
875 | | - swatch.classList.add("selected"); |
876 | | - } |
| 890 | + const swatch = elem( |
| 891 | + "div", |
| 892 | + { |
| 893 | + className: "color-swatch", |
| 894 | + style: { "--swatch-color": hexColor }, |
| 895 | + dataset: color === this.#selectedColor ? { selected: "" } : undefined, |
| 896 | + }, |
| 897 | + [colorButton, elem("div", { className: "label" }, [labelSpan, renameButton])], |
| 898 | + ); |
877 | 899 |
|
878 | | - swatch.addEventListener("click", () => { |
| 900 | + colorButton.addEventListener("click", () => { |
879 | 901 | this.#selectColorFromHistory(color); |
880 | 902 | }); |
881 | 903 |
|
| 904 | + const startRename = (): void => { |
| 905 | + if (labelSpan.isContentEditable) return; |
| 906 | + labelSpan.contentEditable = "plaintext-only"; |
| 907 | + if (labelSpan.dataset.unnamed !== undefined) { |
| 908 | + labelSpan.textContent = ""; |
| 909 | + delete labelSpan.dataset.unnamed; |
| 910 | + } |
| 911 | + |
| 912 | + labelSpan.focus(); |
| 913 | + window.getSelection()?.selectAllChildren(labelSpan); |
| 914 | + }; |
| 915 | + |
| 916 | + const endRename = (cancel = false): void => { |
| 917 | + if (!labelSpan.isContentEditable) return; |
| 918 | + labelSpan.contentEditable = "false"; |
| 919 | + |
| 920 | + const label = labelSpan.textContent; |
| 921 | + if (!cancel && this.#tilemap) { |
| 922 | + if (label) this.#tilemap.colorNames[color] = label; |
| 923 | + else delete this.#tilemap.colorNames[color]; |
| 924 | + } |
| 925 | + |
| 926 | + this.#renderColorHistory(); |
| 927 | + }; |
| 928 | + |
| 929 | + labelSpan.addEventListener("keydown", ev => { |
| 930 | + if (!labelSpan.isContentEditable) return; |
| 931 | + if (ev.key === "Enter") { |
| 932 | + ev.preventDefault(); |
| 933 | + endRename(); |
| 934 | + return; |
| 935 | + } |
| 936 | + |
| 937 | + if (ev.key === "Escape") { |
| 938 | + ev.preventDefault(); |
| 939 | + ev.stopPropagation(); |
| 940 | + endRename(true); |
| 941 | + return; |
| 942 | + } |
| 943 | + }); |
| 944 | + |
| 945 | + labelSpan.addEventListener("blur", () => { |
| 946 | + if (!labelSpan.isContentEditable) return; |
| 947 | + endRename(); |
| 948 | + }); |
| 949 | + |
| 950 | + labelSpan.addEventListener("dblclick", ev => { |
| 951 | + ev.preventDefault(); |
| 952 | + startRename(); |
| 953 | + }); |
| 954 | + renameButton.addEventListener("click", ev => { |
| 955 | + if (ev.button === 0) startRename(); |
| 956 | + }); |
| 957 | + |
882 | 958 | swatchesContainer.appendChild(swatch); |
883 | 959 | } |
884 | 960 | } |
|
0 commit comments