Skip to content

Commit 277099a

Browse files
authored
feat(color-picker): add format dropdown, cancel button, and cancellat… (#2240)
1 parent b3007b1 commit 277099a

5 files changed

Lines changed: 185 additions & 33 deletions

File tree

src/components/settingsPage.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,11 +574,18 @@ async function resolveItemInteraction(item, $target) {
574574
}
575575

576576
if (selectColor) {
577-
const color = await colorPicker(value);
578-
return {
579-
shouldUpdateValue: true,
580-
value: color,
581-
};
577+
try {
578+
const color = await colorPicker(value);
579+
return {
580+
shouldUpdateValue: true,
581+
value: color,
582+
};
583+
} catch (_) {
584+
return {
585+
shouldUpdateValue: false,
586+
shouldCallCallback: false,
587+
};
588+
}
582589
}
583590

584591
if (link) {

src/dialogs/color.js

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,67 @@ let lastPicked = localStorage.__picker_last_picked || "#fff";
1313
function color(defaultColor, onhide) {
1414
defaultColor = defaultColor || lastPicked;
1515
let type = checkColorType(defaultColor) || "hex";
16-
return new Promise((resolve) => {
16+
return new Promise((resolve, reject) => {
1717
const colorModes = ["hsl", "hex", "rgb"];
1818
let mode = colorModes.indexOf(type);
1919
let color = null;
2020

2121
const parent = tag("div", {
2222
className: "message color-picker",
2323
});
24+
25+
const formatToggle = tag("span", {
26+
className: "format-toggle",
27+
onclick: function (e) {
28+
e.preventDefault();
29+
e.stopPropagation();
30+
formatPopup.classList.toggle("visible");
31+
},
32+
children: [
33+
tag("span", {
34+
className: "format-toggle-text",
35+
textContent: type.toUpperCase(),
36+
}),
37+
tag("span", {
38+
className: "icon keyboard_arrow_down",
39+
}),
40+
],
41+
});
42+
43+
const formatPopup = tag("div", {
44+
className: "format-popup",
45+
children: [
46+
tag("div", {
47+
className: "format-popup-backdrop",
48+
onclick: function () {
49+
formatPopup.classList.remove("visible");
50+
},
51+
}),
52+
tag("div", {
53+
className: "format-popup-body",
54+
children: colorModes.map((m) =>
55+
tag("span", {
56+
className: `format-option${m === type ? " active" : ""}`,
57+
textContent: m.toUpperCase(),
58+
onclick: function () {
59+
mode = colorModes.indexOf(m);
60+
type = m;
61+
formatToggle.get(".format-toggle-text").textContent =
62+
m.toUpperCase();
63+
formatPopup.querySelector(".active").classList.remove("active");
64+
this.classList.add("active");
65+
formatPopup.classList.remove("visible");
66+
picker.setOptions({
67+
color: color || defaultColor,
68+
editorFormat: type,
69+
});
70+
},
71+
}),
72+
),
73+
}),
74+
],
75+
});
76+
2477
const okBtn = tag("button", {
2578
textContent: strings.ok,
2679
onclick: function () {
@@ -30,39 +83,39 @@ function color(defaultColor, onhide) {
3083
resolve(color);
3184
},
3285
});
33-
const toggleMode = tag("button", {
34-
textContent: type,
35-
onclick: function (e) {
36-
++mode;
37-
if (mode >= colorModes.length) mode = 0;
38-
type = colorModes[mode];
39-
this.textContent = type;
40-
picker.setOptions({
41-
color,
42-
editorFormat: type,
43-
});
44-
e.preventDefault();
45-
e.stopPropagation();
46-
e.stopImmediatePropagation();
86+
const cancelBtn = tag("button", {
87+
textContent: strings.cancel,
88+
onclick: function () {
89+
hide();
90+
reject(new Error("cancelled"));
4791
},
4892
});
4993
const box = tag("div", {
5094
className: "prompt box",
5195
children: [
52-
tag("strong", {
96+
tag("div", {
5397
className: "title",
54-
textContent: strings["choose color"],
98+
children: [
99+
tag("span", {
100+
className: "title-text",
101+
textContent: strings["choose color"],
102+
}),
103+
formatToggle,
104+
],
55105
}),
56106
parent,
57107
tag("div", {
58108
className: "button-container",
59-
children: [toggleMode, okBtn],
109+
children: [cancelBtn, okBtn],
60110
}),
61111
],
62112
});
63113
const mask = tag("span", {
64114
className: "mask",
65-
onclick: hide,
115+
onclick: function () {
116+
hide();
117+
reject(new Error("cancelled"));
118+
},
66119
});
67120
const picker = new Picker({
68121
parent,
@@ -75,10 +128,14 @@ function color(defaultColor, onhide) {
75128
});
76129

77130
picker.show();
131+
parent.append(formatPopup);
78132

79133
actionStack.push({
80134
id: "box",
81-
action: hideSelect,
135+
action() {
136+
hide();
137+
reject(new Error("cancelled"));
138+
},
82139
});
83140

84141
document.body.append(box, mask);

src/dialogs/style.scss

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,95 @@
139139
min-height: 40px;
140140
margin: 5px 10px 0 10px;
141141
}
142+
143+
.title-text {
144+
flex: 1;
145+
}
146+
147+
.format-toggle {
148+
flex-shrink: 0;
149+
display: inline-flex;
150+
align-items: center;
151+
font-size: 0.55em;
152+
font-weight: 600;
153+
padding: 2px 4px 2px 6px;
154+
border: solid 1px var(--popup-border-color, #ccc);
155+
border-radius: 3px;
156+
color: var(--popup-text-color, #333);
157+
cursor: pointer;
158+
user-select: none;
159+
letter-spacing: 0.5px;
160+
gap: 1px;
161+
162+
.icon.keyboard_arrow_down {
163+
font-size: 1.3em;
164+
opacity: 0.5;
165+
transition: opacity 0.15s;
166+
}
167+
}
142168
}
143169

144170
.message {
145171
overflow: auto;
146172
font-size: 0.9em;
147173

148174
&.color-picker {
175+
position: relative;
176+
149177
.button-container {
150178
margin: 0;
151179
}
180+
181+
.format-popup {
182+
position: absolute;
183+
inset: 0;
184+
display: none;
185+
align-items: center;
186+
justify-content: center;
187+
z-index: 2;
188+
189+
&.visible {
190+
display: flex;
191+
}
192+
193+
.format-popup-backdrop {
194+
position: absolute;
195+
inset: 0;
196+
background: rgba(0, 0, 0, 0.5);
197+
}
198+
199+
.format-popup-body {
200+
position: relative;
201+
display: flex;
202+
gap: 4px;
203+
padding: 6px;
204+
border-radius: 8px;
205+
background: var(--popup-background-color, #fff);
206+
box-shadow: 0 4px 16px var(--box-shadow-color, rgba(0, 0, 0, 0.15));
207+
208+
.format-option {
209+
padding: 6px 14px;
210+
font-size: 12px;
211+
font-weight: 600;
212+
border-radius: 5px;
213+
cursor: pointer;
214+
user-select: none;
215+
color: var(--popup-text-color, #333);
216+
opacity: 0.5;
217+
transition: opacity 0.15s, background-color 0.15s, color 0.15s;
218+
219+
&:hover {
220+
opacity: 0.8;
221+
}
222+
223+
&.active {
224+
opacity: 1;
225+
background-color: var(--button-background-color, #39f);
226+
color: var(--button-text-color, #fff);
227+
}
228+
}
229+
}
230+
}
152231
}
153232

154233
.picker_wrapper {

src/lib/commands.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -458,11 +458,16 @@ export default {
458458

459459
editor.contentDOM.blur();
460460
const wasFocused = editorManager.activeFile.focused;
461-
const res = await color(defaultColor, () => {
462-
if (wasFocused) {
463-
editor.focus();
464-
}
465-
});
461+
let res;
462+
try {
463+
res = await color(defaultColor, () => {
464+
if (wasFocused) {
465+
editor.focus();
466+
}
467+
});
468+
} catch (_) {
469+
return;
470+
}
466471

467472
if (range) {
468473
editor.dispatch({

src/pages/customTheme/customTheme.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,13 @@ export default function CustomThemeInclude() {
9797
className="list-item"
9898
tabindex={0}
9999
onclick={async (e) => {
100-
const newColor = await color(customTheme[key]);
101-
customTheme[key] = newColor;
102-
e.target.get(".icon").style.color = newColor;
100+
try {
101+
const newColor = await color(customTheme[key]);
102+
customTheme[key] = newColor;
103+
e.target.get(".icon").style.color = newColor;
104+
} catch (_) {
105+
/* cancelled */
106+
}
103107
}}
104108
>
105109
<span

0 commit comments

Comments
 (0)