1import { html } from "../html";
2
3const themes = {
4 light: ["latte"],
5 dark: ["frappe", "macchiato", "mocha"],
6};
7
8const allThemes = [...themes.dark, ...themes.light];
9const defaultTheme = "mocha";
10
11function getCurrentTheme(): string {
12 const stored = localStorage.getItem("theme");
13 if (stored && allThemes.includes(stored)) return stored;
14 return defaultTheme;
15}
16
17function getCurrentBoxyMode(): boolean {
18 const stored = localStorage.getItem("boxyMode");
19 return stored === "true";
20}
21
22function applyBoxyMode(isEnabled: boolean) {
23 if (isEnabled) document.documentElement.classList.add("boxy");
24 else document.documentElement.classList.remove("boxy");
25
26 localStorage.setItem("boxyMode", isEnabled.toString());
27}
28
29function applyTheme(theme: string) {
30 for (const t of allThemes) document.body.classList.remove(`theme-${t}`);
31 document.body.classList.add(`theme-${theme}`);
32 localStorage.setItem("theme", theme);
33}
34
35type DropdownOptionItem = {
36 value: string;
37 label: string;
38 selected?: boolean;
39};
40type DropdownOption =
41 | {
42 type: "optgroup";
43 name: string;
44 children: DropdownOptionItem[];
45 }
46 | DropdownOptionItem;
47type DropdownOptions = {
48 options: DropdownOption[];
49 selectedValue?: string;
50 onChange: (value: string) => void;
51 label?: string;
52};
53
54function Dropdown({
55 options,
56 selectedValue,
57 onChange,
58 label = "select an option",
59}: DropdownOptions) {
60 const id = `${Math.random().toString(36).substring(2, 15)}`;
61
62 (window as Window)[`handleDropdownChange_${id}`] = (event: Event) => {
63 const select = event.target as HTMLSelectElement;
64 onChange(select.value);
65 };
66
67 const renderOption = (option: DropdownOptionItem) => {
68 const isSelected = selectedValue === option.value;
69 const _ = html`
70 <option
71 value=${option.value}
72 selected=${isSelected ? "" : undefined}
73 >
74 ${option.label}
75 </option>
76 `;
77 return _;
78 };
79
80 const renderDropdownOption = (option: DropdownOption) => {
81 if ("type" in option && option.type === "optgroup") {
82 return html`
83 <optgroup label="${option.name}">
84 ${option.children.map(renderOption)}
85 </optgroup>
86 `;
87 } else {
88 return renderOption(option as DropdownOptionItem);
89 }
90 };
91
92 return html`
93 <div class="dropdown input-container">
94 <label for="${id}" class="dropdown__label">${label}</label>
95 <select
96 id="${id}"
97 class="dropdown__select"
98 onchange="window.handleDropdownChange_${id}(event)"
99 >
100 ${options.map(renderDropdownOption)}
101 </select>
102 </div>
103 `;
104}
105
106type CheckboxOptions = {
107 label: string;
108 checked: boolean;
109 onChange: (checked: boolean) => void;
110};
111function Checkbox({ label, checked, onChange }: CheckboxOptions) {
112 const id = `${Math.random().toString(36).substring(2, 15)}`;
113
114 (window as Window)[`handleCheckboxChange_${id}`] = (event: Event) => {
115 const checkbox = event.target as HTMLInputElement;
116 onChange(checkbox.checked);
117 };
118
119 return html`
120 <div class="input-container checkbox-container">
121 <span class="checkbox-label" aria-hidden="true">${label}</span>
122 <label class="sr-only" for="${id}">${label}</label>
123 <input
124 type="checkbox"
125 id="${id}"
126 class="checkbox"
127 checked=${checked ? "checked" : ""}
128 onchange="window.handleCheckboxChange_${id}(event)"
129 />
130 </div>
131 `;
132}
133
134export const OptionsSection = () => {
135 const currentTheme = getCurrentTheme();
136 const isBoxyMode = getCurrentBoxyMode();
137
138 applyTheme(currentTheme);
139 applyBoxyMode(isBoxyMode);
140
141 const dropdownOptions: DropdownOptions = {
142 options: [
143 {
144 type: "optgroup",
145 name: "light themes",
146 children: themes.light.map((t) => ({
147 value: t,
148 label: t,
149 selected: t === currentTheme,
150 })),
151 },
152 {
153 type: "optgroup",
154 name: "dark themes",
155 children: themes.dark.map((t) => ({
156 value: t,
157 label: t,
158 selected: t === currentTheme,
159 })),
160 },
161 ],
162 selectedValue: currentTheme,
163 onChange: applyTheme,
164 label: "theme",
165 };
166 const checkboxOptions: CheckboxOptions = {
167 label: "boxy mode",
168 checked: isBoxyMode,
169 onChange: applyBoxyMode,
170 };
171
172 return html`
173 <div class="theme-picker">
174 ${Dropdown(dropdownOptions)}
175 ${Checkbox(checkboxOptions)}
176 </div>
177 `;
178};
179
180document.addEventListener("DOMContentLoaded", () => {
181 applyTheme(getCurrentTheme());
182 applyBoxyMode(getCurrentBoxyMode());
183});