Skip to content

Commit 3e2e191

Browse files
committed
More stuff
1 parent b1b275e commit 3e2e191

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1786
-929
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
import { themeVariablesToCSS, defaultPreset } from 'cx-theme-variables';
3+
4+
interface Props {
5+
theme?: typeof defaultPreset;
6+
}
7+
8+
const { theme = defaultPreset } = Astro.props;
9+
const css = themeVariablesToCSS(theme);
10+
---
11+
12+
<style set:html={css}></style>

homedocs/src/examples/forms/ColorFieldExample.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ export default (
2121
<ColorField label="Hex Format" value={m.hex} format="hex" />
2222
<ColorField label="Disabled" value={m.color} disabled />
2323
<ColorField label="Read-only" value={m.color} readOnly />
24-
<ColorField label="Placeholder" value={m.placeholder} placeholder="Pick a color..." />
24+
<ColorField
25+
label="Placeholder"
26+
value={m.placeholder}
27+
placeholder="Pick a color..."
28+
/>
2529
</LabelsTopLayout>
2630
);
2731
// @index-end

homedocs/src/examples/theme-editor/Preview.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
Prop,
77
} from "cx/ui";
88
import { Repeater } from "cx/widgets";
9-
import { Category, m } from "./model";
9+
import { ThemeVarsDiv } from "cx-theme-variables";
10+
import { m } from "./model";
1011
import { examples, ExampleDef } from "./examples";
12+
import { categoriesToTheme, fontOptions } from "./data";
1113

1214
// Dynamic example content using ContentResolver
1315
const ExampleContent = createFunctionalComponent(
@@ -16,11 +18,7 @@ const ExampleContent = createFunctionalComponent(
1618
params={{ example }}
1719
onResolve={async ({ example }) => {
1820
const module = await example.component();
19-
return (
20-
<PrivateStore>
21-
{module.default}
22-
</PrivateStore>
23-
);
21+
return <PrivateStore>{module.default}</PrivateStore>;
2422
}}
2523
/>
2624
),
@@ -31,38 +29,50 @@ function getExamplesForCategory(category: string): ExampleDef[] {
3129
return examples.filter((e) => e.categories.includes(category));
3230
}
3331

34-
// Build CSS variables style object from categories
35-
function buildCssVariables(categories: Category[]): Record<string, string> {
36-
const style: Record<string, string> = {};
37-
for (const cat of categories) {
38-
for (const v of cat.variables) {
39-
style[v.name] = v.value;
40-
}
41-
}
42-
return style;
32+
// Get Google Font URL for the selected font
33+
function getGoogleFontUrl(fontId: string | null): string | undefined {
34+
if (!fontId) return undefined;
35+
// website default font doesn't have to be loaded
36+
if (fontId == "inter") return undefined;
37+
const font = fontOptions.find((f) => f.id === fontId);
38+
return font?.googleFont
39+
? `https://fonts.googleapis.com/css2?family=${font.googleFont}&display=swap`
40+
: undefined;
4341
}
4442

4543
export const Preview = createFunctionalComponent(() => (
46-
<div
47-
class="flex-1 p-6 overflow-y-auto bg-background border-x border-border"
48-
style={computable(m.categories, buildCssVariables)}
44+
<ThemeVarsDiv
45+
theme={computable(m.categories, categoriesToTheme)}
46+
class="flex-1 p-6 overflow-y-auto border-r border-border"
47+
applyReset
4948
>
49+
<link
50+
href={computable(m.font, getGoogleFontUrl)}
51+
rel="stylesheet"
52+
visible={computable(m.font, (f) => !!getGoogleFontUrl(f))}
53+
/>
5054
<Repeater
5155
records={computable(m.activeCategory, (category) =>
5256
getExamplesForCategory(category),
5357
)}
5458
recordAlias={m.$example}
5559
keyField="id"
5660
>
57-
<div class="mb-6 max-w-[600px] mx-auto">
61+
<div class="mb-6 max-w-150 mx-auto">
5862
<h4
5963
class="text-sm font-medium text-muted-foreground mb-3"
6064
text={m.$example.name}
6165
/>
62-
<div class="bg-card border border-border rounded-lg p-4 overflow-x-auto">
66+
<div
67+
class="border border-border rounded-lg p-4 overflow-x-auto"
68+
style={{
69+
backgroundColor: "var(--cx-theme-surface-color)",
70+
borderColor: "var(--cx-theme-border-color)",
71+
}}
72+
>
6373
<ExampleContent example={m.$example} />
6474
</div>
6575
</div>
6676
</Repeater>
67-
</div>
77+
</ThemeVarsDiv>
6878
));
Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
11
import { createFunctionalComponent, expr } from "cx/ui";
2-
import { Icon, Repeater } from "cx/widgets";
2+
import { Icon } from "cx/widgets";
33
import { m } from "./model";
4+
import { categoryGroups } from "./data";
45

56
export const Sidebar = createFunctionalComponent(() => (
6-
<div class="w-52 border-x border-border py-4 overflow-y-auto bg-sidebar">
7-
<Repeater records={m.categories} recordAlias={m.$category}>
8-
<div
9-
class={{
10-
"px-4 py-2.5 cursor-pointer flex items-center gap-2.5 text-sm transition-colors": true,
11-
"hover:bg-secondary": true,
12-
"bg-accent text-accent-foreground": expr(m.$category.id, m.activeCategory, (id, active) => id === active),
13-
"text-sidebar-foreground": expr(m.$category.id, m.activeCategory, (id, active) => id !== active),
14-
}}
15-
onClick={(e: any, { store }: any) => {
16-
store.set(m.activeCategory, store.get(m.$category.id));
17-
}}
18-
>
19-
<Icon name={m.$category.icon} />
20-
<span text={m.$category.name} />
7+
<div class="w-52 border-x border-border overflow-y-auto bg-sidebar">
8+
{categoryGroups.map((group) => (
9+
<div key={group.name}>
10+
<div class="px-4 pt-4 pb-2 mt-2 first:mt-0 text-xs font-semibold text-foreground uppercase tracking-wide">
11+
{group.name}
12+
</div>
13+
{group.categories.map((cat) => (
14+
<div
15+
key={cat.id}
16+
class={{
17+
"px-4 py-2.5 cursor-pointer flex items-center gap-2.5 text-sm transition-colors": true,
18+
"hover:bg-secondary": true,
19+
"bg-accent text-accent-foreground": expr(
20+
m.activeCategory,
21+
(active) => cat.id === active,
22+
),
23+
"text-sidebar-foreground": expr(
24+
m.activeCategory,
25+
(active) => cat.id !== active,
26+
),
27+
}}
28+
onClick={(e: any, { store }: any) => {
29+
store.set(m.activeCategory, cat.id);
30+
}}
31+
>
32+
<Icon name={cat.icon} />
33+
<span>{cat.name}</span>
34+
</div>
35+
))}
2136
</div>
22-
</Repeater>
37+
))}
2338
</div>
2439
));

homedocs/src/examples/theme-editor/ThemeEditor.tsx

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,82 @@ import { Button, LookupField } from "cx/widgets";
33

44
import "../../icons/lucide";
55
import { m, ThemeEditorModel } from "./model";
6-
import { defaultCategories, presets } from "./data";
6+
import {
7+
presets,
8+
roundingOptions,
9+
densityOptions,
10+
fontOptions,
11+
themeToCategories,
12+
} from "./data";
713
import { Sidebar } from "./Sidebar";
814
import { VariableEditor } from "./VariableEditor";
915
import { Preview } from "./Preview";
1016

1117
class ThemeEditorController extends Controller<ThemeEditorModel> {
1218
onInit() {
13-
this.store.init(m.categories, defaultCategories);
14-
this.store.init(m.activeCategory, "primary");
15-
this.store.init(m.presetName, "dark-blue");
19+
this.store.init(m.activeCategory, "colors");
20+
this.store.init(m.presetName, "default");
21+
22+
this.addTrigger(
23+
"theme-change",
24+
[m.presetName, m.rounding, m.density, m.font],
25+
(presetName, rounding, density, font) => {
26+
const preset = presets.find((p) => p.id === presetName);
27+
const roundingTweak = rounding
28+
? roundingOptions.find((r) => r.id === rounding)
29+
: null;
30+
const densityTweak = density
31+
? densityOptions.find((d) => d.id === density)
32+
: null;
33+
const fontTweak = font ? fontOptions.find((f) => f.id === font) : null;
34+
if (preset) {
35+
const theme = {
36+
...preset.theme,
37+
...roundingTweak?.tweak,
38+
...densityTweak?.tweak,
39+
...fontTweak?.tweak,
40+
};
41+
this.store.set(m.categories, themeToCategories(theme));
42+
}
43+
},
44+
true,
45+
);
1646
}
1747

1848
copyToClipboard() {
1949
const categories = this.store.get(m.categories);
2050
let css = ":root {\n";
2151
for (const cat of categories) {
2252
for (const v of cat.variables) {
23-
css += ` ${v.name}: ${v.value};\n`;
53+
css += ` ${v.key}: ${v.value};\n`;
2454
}
2555
}
2656
css += "}\n";
2757
navigator.clipboard.writeText(css);
2858
}
2959

3060
reset() {
31-
this.store.set(m.categories, defaultCategories);
61+
const presetName = this.store.get(m.presetName);
62+
const rounding = this.store.get(m.rounding);
63+
const density = this.store.get(m.density);
64+
const font = this.store.get(m.font);
65+
const preset = presets.find((p) => p.id === presetName);
66+
const roundingTweak = rounding
67+
? roundingOptions.find((r) => r.id === rounding)
68+
: null;
69+
const densityTweak = density
70+
? densityOptions.find((d) => d.id === density)
71+
: null;
72+
const fontTweak = font ? fontOptions.find((f) => f.id === font) : null;
73+
if (preset) {
74+
const theme = {
75+
...preset.theme,
76+
...roundingTweak?.tweak,
77+
...densityTweak?.tweak,
78+
...fontTweak?.tweak,
79+
};
80+
this.store.set(m.categories, themeToCategories(theme));
81+
}
3282
}
3383
}
3484

@@ -41,11 +91,42 @@ export default (
4191
<div class="border-b border-border bg-card">
4292
<div class="container mx-auto flex items-center gap-4 px-4 py-3">
4393
<span class="text-sm text-muted-foreground">Preset:</span>
44-
<LookupField value={m.presetName} options={presets} style="width: 180px;" />
94+
<LookupField
95+
value={m.presetName}
96+
options={presets}
97+
required
98+
style="width: 140px;"
99+
/>
100+
<span class="text-sm text-muted-foreground">Rounding:</span>
101+
<LookupField
102+
value={m.rounding}
103+
options={roundingOptions}
104+
placeholder="Use default"
105+
style="width: 140px;"
106+
/>
107+
<span class="text-sm text-muted-foreground">Density:</span>
108+
<LookupField
109+
value={m.density}
110+
options={densityOptions}
111+
placeholder="Use default"
112+
style="width: 140px;"
113+
/>
114+
<span class="text-sm text-muted-foreground">Font:</span>
115+
<LookupField
116+
value={m.font}
117+
options={fontOptions}
118+
placeholder="Use default"
119+
style="width: 140px;"
120+
/>
45121
<div class="flex-1" />
46122
<Button icon="refresh" text="Reset" onClick="reset" mod="hollow" />
47123
<Button icon="download" text="Download" mod="hollow" />
48-
<Button icon="copy" text="Copy to Clipboard" onClick="copyToClipboard" mod="primary" />
124+
<Button
125+
icon="copy"
126+
text="Copy to Clipboard"
127+
onClick="copyToClipboard"
128+
mod="primary"
129+
/>
49130
</div>
50131
</div>
51132

homedocs/src/examples/theme-editor/VariableEditor.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@ export const VariableEditor = createFunctionalComponent(() => (
7171
<Repeater records={m.$category.variables} recordAlias={m.$variable}>
7272
<div class="mb-5">
7373
<div
74-
class="text-sm font-mono text-foreground mb-0.5"
75-
text={m.$variable.name}
76-
/>
77-
<div
78-
class="text-xs text-muted-foreground mb-2"
74+
class="text-xs text-foreground mb-1"
7975
text={m.$variable.label}
8076
/>
8177
<div class="flex gap-2 items-center">

0 commit comments

Comments
 (0)