Skip to content

Commit 8fea4ea

Browse files
authored
FEATURE: redesigned admin color palette index layout (#33628)
This introduces a new layout for the color palettes admin page (similar to the layout of /admin/config/customize/themes), and includes some new features! <img width="2222" height="1540" alt="image" src="https://github.com/user-attachments/assets/971f3b90-b3c9-48be-9fa9-fb40a28a3911" /> * Grid based cards with dynamic preview image * If you have more than 8 palettes, a filter will appear * From /admin/customize/colors you can toggle user-selectability, make a palette active on the current default theme, or delete it * Each card lists user selectable status and links associated theme (when present) * The seeded Light (default) theme now appears in the list to make it easy to toggle back to This doesn't include changes to the palette editing experience, which now lives on a stand-alone page (same `/admin/customize/colors/{id}` route)
1 parent 66adafd commit 8fea4ea

17 files changed

+1260
-131
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
import Component from "@glimmer/component";
2+
import { tracked } from "@glimmer/tracking";
3+
import { array, fn } from "@ember/helper";
4+
import { action } from "@ember/object";
5+
import { LinkTo } from "@ember/routing";
6+
import { htmlSafe } from "@ember/template";
7+
import { not } from "truth-helpers";
8+
import DButton from "discourse/components/d-button";
9+
import DropdownMenu from "discourse/components/dropdown-menu";
10+
import icon from "discourse/helpers/d-icon";
11+
import { i18n } from "discourse-i18n";
12+
import SvgSingleColorPalettePlaceholder from "admin/components/svg/single-color-palette-placeholder";
13+
import { getColorSchemeStyles } from "admin/lib/color-transformations";
14+
import DButtonTooltip from "float-kit/components/d-button-tooltip";
15+
import DMenu from "float-kit/components/d-menu";
16+
import DTooltip from "float-kit/components/d-tooltip";
17+
18+
export default class ColorPaletteListItem extends Component {
19+
@tracked isLoading = false;
20+
21+
get isBuiltInDefault() {
22+
return this.args.scheme?.is_builtin_default || false;
23+
}
24+
25+
get setAsDefaultLabel() {
26+
const themeName = this.args.defaultTheme?.name || "Default";
27+
28+
return i18n("admin.customize.colors.set_default", {
29+
theme: themeName,
30+
});
31+
}
32+
33+
get canEdit() {
34+
return !this.isBuiltInDefault && this.args.scheme?.id;
35+
}
36+
37+
get canDelete() {
38+
return !this.isBuiltInDefault && !this.args.scheme?.theme_id;
39+
}
40+
41+
get showSetAsDefault() {
42+
if (this.isBuiltInDefault) {
43+
return this.args.defaultTheme?.color_scheme_id;
44+
}
45+
return true;
46+
}
47+
48+
get isActive() {
49+
if (this.isBuiltInDefault) {
50+
return this.args.defaultTheme && !this.args.defaultTheme.color_scheme_id;
51+
}
52+
return (
53+
this.args.defaultTheme &&
54+
this.args.isDefaultThemeColorScheme(this.args.scheme)
55+
);
56+
}
57+
58+
get styles() {
59+
if (this.isBuiltInDefault) {
60+
return htmlSafe(
61+
"--primary-low--preview: #e9e9e9; --tertiary-low--preview: #d1f0ff;"
62+
);
63+
}
64+
65+
// generate primary-low and tertiary-low
66+
const existingStyles = getColorSchemeStyles(this.args.scheme);
67+
68+
// create variables from scheme.colors
69+
const colorVariables =
70+
this.args.scheme?.colors
71+
?.map((color) => {
72+
let hex = color.hex || color.default_hex;
73+
74+
if (hex && !hex.startsWith("#")) {
75+
hex = `#${hex}`;
76+
}
77+
return `--${color.name}--preview: ${hex}`;
78+
})
79+
.join("; ") || "";
80+
81+
const allStyles = colorVariables
82+
? `${existingStyles} ${colorVariables};`
83+
: existingStyles;
84+
85+
return htmlSafe(allStyles);
86+
}
87+
88+
@action
89+
async handleAsyncAction(asyncFn, ...args) {
90+
this.dMenu.close();
91+
this.isLoading = true;
92+
try {
93+
await asyncFn(...args);
94+
} finally {
95+
this.isLoading = false;
96+
}
97+
}
98+
99+
@action
100+
onRegisterApi(api) {
101+
this.dMenu = api;
102+
}
103+
104+
<template>
105+
<li
106+
style={{this.styles}}
107+
class="admin-config-area-card color-palette"
108+
data-palette-id={{@scheme.id}}
109+
>
110+
<div class="color-palette__container">
111+
<div class="color-palette__preview">
112+
<SvgSingleColorPalettePlaceholder />
113+
</div>
114+
115+
<div class="color-palette__details">
116+
<h3>{{@scheme.description}}</h3>
117+
<div class="color-palette__theme-link">
118+
{{#if @scheme.theme_id}}
119+
<LinkTo
120+
@route="adminCustomizeThemes.show"
121+
@models={{array "themes" @scheme.theme_id}}
122+
>
123+
{{icon "link"}}
124+
{{@scheme.theme_name}}
125+
</LinkTo>
126+
{{/if}}
127+
</div>
128+
129+
<div class="color-palette__badges">
130+
{{#if @scheme.user_selectable}}
131+
<span
132+
title={{i18n "admin.customize.theme.user_selectable"}}
133+
class="theme-card__badge --selectable"
134+
>
135+
{{icon "user-check"}}
136+
{{i18n "admin.customize.theme.user_selectable_badge_label"}}
137+
</span>
138+
{{/if}}
139+
</div>
140+
141+
{{#if this.isActive}}
142+
<span
143+
title={{i18n "admin.customize.colors.active_badge.title"}}
144+
class="theme-card__badge --active"
145+
>
146+
{{i18n "admin.customize.colors.active_badge.text"}}
147+
</span>
148+
{{/if}}
149+
</div>
150+
151+
<div class="color-palette__controls">
152+
<DButtonTooltip>
153+
<:button>
154+
<DButton
155+
@route="adminCustomize.colors-show"
156+
@routeModels={{array @scheme.id}}
157+
@label="admin.customize.colors.edit"
158+
class="btn-secondary"
159+
@disabled={{not this.canEdit}}
160+
/>
161+
</:button>
162+
<:tooltip>
163+
{{#unless this.canEdit}}
164+
<DTooltip
165+
@icon="circle-info"
166+
@content={{i18n "admin.customize.colors.system_palette"}}
167+
/>
168+
{{/unless}}
169+
</:tooltip>
170+
</DButtonTooltip>
171+
172+
{{#if this.showSetAsDefault}}
173+
<DMenu
174+
@triggerClass="btn-flat"
175+
@modalForMobile={{true}}
176+
@icon="ellipsis"
177+
@triggers={{array "click"}}
178+
@onRegisterApi={{this.onRegisterApi}}
179+
@isLoading={{this.isLoading}}
180+
>
181+
<:content>
182+
<DropdownMenu as |dropdown|>
183+
{{#unless this.isBuiltInDefault}}
184+
<dropdown.item>
185+
<DButton
186+
@action={{fn
187+
this.handleAsyncAction
188+
@toggleUserSelectable
189+
@scheme
190+
}}
191+
@icon={{if
192+
@scheme.user_selectable
193+
"user-xmark"
194+
"user-check"
195+
}}
196+
@label={{if
197+
@scheme.user_selectable
198+
"admin.customize.theme.user_selectable_unavailable_button_label"
199+
"admin.customize.theme.user_selectable_button_label"
200+
}}
201+
class="btn-transparent"
202+
/>
203+
</dropdown.item>
204+
{{/unless}}
205+
206+
<dropdown.item>
207+
<DButton
208+
@action={{fn
209+
this.handleAsyncAction
210+
@setAsDefaultThemePalette
211+
@scheme
212+
}}
213+
@icon="star"
214+
@translatedLabel={{this.setAsDefaultLabel}}
215+
class="btn-transparent btn-palette-default"
216+
disabled={{this.isActive}}
217+
/>
218+
</dropdown.item>
219+
220+
{{#if this.canDelete}}
221+
<dropdown.item>
222+
<DButton
223+
@action={{fn
224+
this.handleAsyncAction
225+
@deleteColorScheme
226+
@scheme
227+
}}
228+
@icon="trash-can"
229+
@label="admin.customize.delete"
230+
class="btn-transparent btn-danger"
231+
/>
232+
</dropdown.item>
233+
{{/if}}
234+
</DropdownMenu>
235+
</:content>
236+
</DMenu>
237+
{{/if}}
238+
</div>
239+
</div>
240+
</li>
241+
</template>
242+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
const SvgSingleColorPalettePlaceholder = <template>
2+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 558 171">
3+
<rect
4+
width="120"
5+
height="25.616"
6+
x="436.298"
7+
y="95.819"
8+
fill="var(--primary-low--preview, #e9e9e9)"
9+
rx="4"
10+
/>
11+
<rect
12+
width="120"
13+
height="24.64"
14+
x="436.298"
15+
y="144.442"
16+
fill="var(--tertiary--preview, #08c)"
17+
rx="4"
18+
/>
19+
<rect
20+
width="555.265"
21+
height="71.394"
22+
x=".22"
23+
y=".341"
24+
fill="var(--tertiary-low--preview, #d1f0ff)"
25+
rx="4"
26+
/>
27+
<rect
28+
width="30"
29+
height="24.64"
30+
x="23.451"
31+
y="145.418"
32+
fill="var(--danger--preview, #c80001)"
33+
rx="4"
34+
/>
35+
<rect
36+
width="30"
37+
height="24.64"
38+
x="88.578"
39+
y="145.418"
40+
fill="var(--success--preview, #090)"
41+
rx="4"
42+
/>
43+
<rect
44+
width="30"
45+
height="24.64"
46+
x="153.704"
47+
y="145.418"
48+
fill="var(--love--preview, #fa6c8d)"
49+
rx="4"
50+
/>
51+
<rect
52+
width="30"
53+
height="24.64"
54+
x="218.831"
55+
y="145.418"
56+
fill="var(--highlight--preview, #ffff4d)"
57+
rx="4"
58+
/>
59+
<rect
60+
width="200.35"
61+
height="4.526"
62+
x="23.451"
63+
y="43.772"
64+
fill="var(--primary--preview, #222)"
65+
rx="2.263"
66+
/>
67+
<rect
68+
width="333.847"
69+
height="4.526"
70+
x="23.451"
71+
y="66.295"
72+
fill="var(--primary--preview, #222)"
73+
rx="2.263"
74+
/>
75+
<rect
76+
width="267.897"
77+
height="4.526"
78+
x="23.451"
79+
y="90.819"
80+
fill="var(--primary--preview, #222)"
81+
rx="2.263"
82+
/>
83+
<rect
84+
width="299.961"
85+
height="4.526"
86+
x="23.451"
87+
y="116.342"
88+
fill="var(--primary--preview, #222)"
89+
rx="2.263"
90+
/>
91+
</svg>
92+
</template>;
93+
94+
export default SvgSingleColorPalettePlaceholder;

app/assets/javascripts/admin/addon/controllers/admin-customize-colors-show.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default class AdminCustomizeColorsShowController extends Controller {
6060
);
6161
newColorScheme.save().then(() => {
6262
this.allColors.pushObject(newColorScheme);
63-
this.router.replaceWith("adminCustomize.colors.show", newColorScheme);
63+
this.router.replaceWith("adminCustomize.colors-show", newColorScheme);
6464
});
6565
}
6666

0 commit comments

Comments
 (0)