From d06784958d73159b5e4abafe5114804662114ed7 Mon Sep 17 00:00:00 2001 From: Ryan Schanzenbacher Date: Thu, 9 Jul 2026 22:15:53 -0400 Subject: move ubersicht modules in tree --- .../settings/aerospace-display-manager.jsx | 83 ++++++ .../lib/components/settings/color-picker.jsx | 109 ++++++++ .../lib/components/settings/icon-picker.jsx | 130 +++++++++ .../lib/components/settings/settings-component.jsx | 159 +++++++++++ .../lib/components/settings/settings-inner.jsx | 123 +++++++++ .../lib/components/settings/settings-item.jsx | 143 ++++++++++ .../lib/components/settings/settings-widgets.jsx | 180 +++++++++++++ .../lib/components/settings/settings.jsx | 94 +++++++ .../components/settings/user-widgets-creator.jsx | 297 +++++++++++++++++++++ 9 files changed, 1318 insertions(+) create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/aerospace-display-manager.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/color-picker.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/icon-picker.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-component.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-inner.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-item.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-widgets.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings') diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/aerospace-display-manager.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/aerospace-display-manager.jsx new file mode 100755 index 0000000..8c4fa98 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/aerospace-display-manager.jsx @@ -0,0 +1,83 @@ +import * as Uebersicht from "uebersicht"; +import * as Utils from "../../utils"; +import * as Icons from "../icons/icons.jsx"; + +const { React } = Uebersicht; + +/** + * UserWidgetsCreator component allows users to create and manage custom widgets. + * @param {Object} props - The component props. + * @param {Object} props.defaultValue - The default value for the widgets. + * @param {Function} props.onChange - The function to call when the widgets change. + * @returns {JSX.Element} The UserWidgetsCreator component. + */ +export default function AerospaceDisplayManager({ defaultValue, onChange }) { + const [indexes, setIndexes] = React.useState(defaultValue || {}); + const [displays, setDisplays] = React.useState(Object.keys(defaultValue)); + + const addDisplay = () => { + setDisplays((current) => { + const highest = Math.max(...current, 0); + const newIndex = highest + 1; + setIndexes((current) => { + return { ...current, [newIndex]: Number(current[newIndex]) || 1 }; + }); + return [...current, newIndex]; + }); + }; + + const removeDisplay = (index) => { + setDisplays((current) => { + const newDisplays = current.filter((display) => display !== index); + setIndexes((current) => { + const newIndexes = { ...current }; + delete newIndexes[index]; + return newIndexes; + }); + return newDisplays; + }); + }; + + React.useEffect(() => { + const diffs = Utils.compareObjects(defaultValue, indexes); + const hasDiffs = Object.keys(diffs).length > 0; + if (hasDiffs) onChange({ target: { value: indexes } }); + }, [defaultValue, onChange, indexes]); + + return ( +
+
+ AeroSpace Custom display indexes +
+
+ {displays.map((display) => { + const index = indexes[display] || 1; + + const updateIndex = (e) => { + const value = e.target.value; + setIndexes((current) => { + return { ...current, [display]: Number(value) }; + }); + }; + + return ( +
+ {display}: + + +
+ ); + })} +
+ +
+ ); +} diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/color-picker.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/color-picker.jsx new file mode 100755 index 0000000..dbff879 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/color-picker.jsx @@ -0,0 +1,109 @@ +import * as Uebersicht from "uebersicht"; +import * as Settings from "../../settings"; + +const { React } = Uebersicht; + +/** + * ColorPicker component allows users to select a color from predefined options or input a custom color. + * @param {Object} props - The component props. + * @param {Function} props.callback - The callback function to handle color selection. + * @param {number} props.index - The index of the color picker. + * @param {string} props.selectedColor - The initially selected color. + * @returns {JSX.Element} The ColorPicker component. + */ +export default function ColorPicker({ callback, index, selectedColor }) { + // Determine if the selected color is a custom color + const isSelectedCustom = !Settings.userWidgetColors.includes(selectedColor); + // State to manage the open/close status of the color picker + const [open, setOpen] = React.useState(false); + // State to manage the currently selected color + const [selected, setSelected] = React.useState(selectedColor); + // State to manage the custom color input + const [customColor, setCustomColor] = React.useState( + isSelectedCustom ? selectedColor : undefined, + ); + + // Toggle the color picker open/close status + const onClick = () => setOpen(!open); + + /** + * Handle custom color input change. + * @param {Object} e - The event object. + */ + const onCustomColorChange = (e) => { + const value = e.target.value; + setCustomColor(value); + callback?.(index, "backgroundColor", value); + }; + + /** + * Handle custom color submission. + */ + const onCustomColorSubmit = () => { + setSelected(customColor); + setOpen(false); + }; + + return ( +
+ +
+ + )} + + ); +} diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/icon-picker.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/icon-picker.jsx new file mode 100755 index 0000000..ca3bdab --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/icon-picker.jsx @@ -0,0 +1,130 @@ +import * as Uebersicht from "uebersicht"; +import * as Icons from "../icons/icons.jsx"; +import { SuspenseIcon } from "../icons/icon.jsx"; + +const { React } = Uebersicht; + +/** + * IconPicker component allows users to select an icon from a list of available icons. + * + * @param {Object} props - The component props. + * @param {Function} props.callback - The callback function to be called when an icon is selected. + * @param {number} props.index - The index of the icon picker. + * @param {string} props.selectedIcon - The initially selected icon. + * @returns {JSX.Element} The IconPicker component. + */ +export default function IconPicker({ callback, index, selectedIcon }) { + // State to manage the open/close state of the icon picker dropdown + const [open, setOpen] = React.useState(false); + // State to manage the currently selected icon + const [selected, setSelected] = React.useState(selectedIcon); + // State to manage the search term for filtering icons + const [searchTerm, setSearchTerm] = React.useState(""); + + // Get the selected icon component + const Icon = Icons[selected]; + // Get the list of all available icon keys + const keys = Object.keys(Icons); + + // Filter icons based on search term + const filteredKeys = React.useMemo(() => { + if (!searchTerm.trim()) { + return keys; + } + return keys.filter((key) => + key.toLowerCase().includes(searchTerm.toLowerCase()), + ); + }, [keys, searchTerm]); + + /** + * Toggles the open state of the icon picker dropdown. + */ + const onClick = () => setOpen(!open); + + /** + * Handles search input changes. + * + * @param {React.ChangeEvent} e - The input change event. + */ + const onSearchChange = (e) => { + setSearchTerm(e.target.value); + }; + + /** + * Clears the search term. + */ + const clearSearch = () => { + setSearchTerm(""); + }; + + return ( +
+ + {open && ( +
+
e.stopPropagation()} + > + + + {searchTerm && ( + + )} +
+
setOpen(false)}> + {filteredKeys.length > 0 ? ( + filteredKeys.map((key) => { + /** + * Handles the icon selection. + * + * @param {React.MouseEvent} e - The click event. + */ + const onClick = (e) => { + e.stopPropagation(); + setSelected(key); + callback?.(index, "icon", key); + setOpen(false); + }; + const Icon = Icons[key]; + return ( + + ); + }) + ) : ( +
+ No icons found for "{searchTerm}" +
+ )} +
+
+ )} +
+ ); +} diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-component.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-component.jsx new file mode 100755 index 0000000..8e48b66 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-component.jsx @@ -0,0 +1,159 @@ +import * as Uebersicht from "uebersicht"; +import * as Utils from "../../utils"; +import SettingsInner from "./settings-inner.jsx"; +import SettingsWidgets from "./settings-widgets.jsx"; +import * as Settings from "../../settings"; + +const { React } = Uebersicht; + +const TABS_STORAGE_KEY = "simple-bar-last-current-settings-tab"; +const TABS = [ + "global", + "themes", + "process", + "spacesDisplay", + "widgets", + "customStyles", +]; + +/** + * Main settings component. + * @param {Object} props - Component properties. + * @param {Function} props.closeSettings - Function to close the settings. + */ +export default function Component({ closeSettings }) { + // State to keep track of the current tab + const [currentTab, setCurrentTab] = React.useState(getLastCurrentTab()); + // State to keep track of pending changes + const [pendingChanges, setPendingChanges] = React.useState(0); + // Get current settings + const settings = Settings.get(); + // State to keep track of new settings + const [newSettings, setNewSettings] = React.useState(settings); + + /** + * Update the current tab and store it in session storage. + * @param {number} tab - The index of the tab to switch to. + */ + const updateTab = (tab) => { + setCurrentTab(tab); + window.sessionStorage.setItem(TABS_STORAGE_KEY, tab); + }; + + /** + * Refresh the simple-bar with new settings. + * @param {Event} e - The event object. + */ + const refreshSimpleBar = async (e) => { + Utils.clickEffect(e); + setPendingChanges(0); + await Settings.set(newSettings); + Utils.hardRefresh(); + }; + + // Effect to calculate the number of pending changes + React.useEffect(() => { + const diffs = Utils.compareObjects(settings, newSettings); + const deepDiffs = Object.keys(diffs).reduce( + (acc, key) => [...acc, ...Object.keys(diffs[key])], + [], + ); + setPendingChanges(deepDiffs.length); + }, [newSettings, settings]); + + return ( +
+
+
+
+
+
+ {Object.keys(Settings.defaultSettings).map((key) => { + const setting = Settings.data[key]; + const hideTab = !TABS.includes(key); + + if (!setting || hideTab) return null; + + const { label } = setting; + const i = TABS.indexOf(key); + const classes = Utils.classNames("settings__tab", { + "settings__tab--current": i === currentTab, + }); + return ( + + ); + })} +
+
+ {Object.keys(Settings.defaultSettings).map((key) => { + const setting = Settings.data[key]; + const hideContent = !TABS.includes(key); + + if (!setting || hideContent) return null; + + const isWidgetsTab = key === "widgets"; + + const { label } = setting; + return ( +
+ {isWidgetsTab ? ( + + ) : ( + +
{label}
+ +
+ )} +
+ ); + })} +
+
+ {pendingChanges !== 0 && ( +
+ {pendingChanges} pending change{pendingChanges > 1 && "s"} +
+ )} + +
+
+
+ ); +} + +/** + * Get the last current tab from session storage. + * @returns {number} The index of the last current tab. + */ +function getLastCurrentTab() { + const storedLastCurrentTab = window.sessionStorage.getItem(TABS_STORAGE_KEY); + if (storedLastCurrentTab) return parseInt(storedLastCurrentTab, 10); + return 0; +} diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-inner.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-inner.jsx new file mode 100755 index 0000000..e318b25 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-inner.jsx @@ -0,0 +1,123 @@ +import * as Uebersicht from "uebersicht"; +import * as Icons from "../icons/icons.jsx"; +import SettingsItem from "./settings-item.jsx"; +import * as Utils from "../../utils"; +import * as Settings from "../../settings"; + +const { React } = Uebersicht; + +/** + * SettingsInner component to render each setting category. + * @param {Object} props - Component properties. + * @param {string} props.settingKey - Key of current setting. + * @param {Object} props.setting - Contains infos & documentation data. + * @param {string[]} props.setting.infos - Current settings information. + * @param {string} props.setting.documentation - Current setting documentation link. + * @param {Object} props.newSettings - Object containing current modified settings. + * @param {Function} props.setNewSettings - Function allowing to save newly modified settings. + */ +export default function SettingsInner({ + settingKey, + setting, + newSettings, + setNewSettings, +}) { + const { infos, documentation } = setting; + return ( + <> + {Object.keys(Settings.defaultSettings[settingKey]).map((subKey) => { + const subSetting = Settings.data[subKey]; + if (!subSetting) return null; + const { + Component, + fullWidth, + label, + options, + placeholder, + title, + type, + minHeight, + } = subSetting; + + const code = settingKey + subKey; + const defaultValue = newSettings[settingKey][subKey]; + + const classes = Utils.classNames("settings__item", { + "settings__item--radio": type === "radio", + "settings__item--text": + type === "text" || type === "number" || type === "color", + "settings__item--textarea": type === "textarea", + "settings__item--color": type === "color", + "settings__item--full-width": fullWidth, + }); + + const onChange = (e) => { + let value = e.target.value; + if (type === "checkbox") { + value = e.target.checked; + } + if (type === "number") { + value = parseFloat(value); + if (isNaN(value)) { + value = 0; + } + } + + const updatedSettings = { + ...newSettings, + [settingKey]: { ...newSettings[settingKey], [subKey]: value }, + }; + setNewSettings(updatedSettings); + }; + + return ( + + {title &&
{title}
} +
+ +
+
+ ); + })} + {documentation && ( +
+ + + You{"'"}ll find all the information about these settings{" "} + + here on the documentation + {" "} + hosted on jeantinland.com. + +
+ )} + {infos && infos.length && ( +
+
Tips
+ {infos.map((info, i) => ( +
+ ))} +
+ )} + + ); +} diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-item.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-item.jsx new file mode 100755 index 0000000..91c6f54 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/settings-item.jsx @@ -0,0 +1,143 @@ +import * as Uebersicht from "uebersicht"; +import * as Utils from "../../utils"; + +const { React } = Uebersicht; + +/** + * Item component to render different types of settings inputs. + * @param {Object} props - Component properties. + * @param {string} props.code - Unique code for the setting. + * @param {React.Component} props.Component - Custom component for the setting. + * @param {any} props.defaultValue - Default value of the setting. + * @param {string} props.label - Label for the setting. + * @param {string} props.type - Type of the setting input. + * @param {Array} [props.options] - Options for select or radio inputs. + * @param {string} [props.placeholder] - Placeholder for text inputs. + * @param {number} [props.minHeight] - Minimum height for textarea inputs. + * @param {Function} props.onChange - Change handler for the setting input. + */ +export default function SettingsItem({ + code, + Component, + defaultValue, + label, + type, + options, + placeholder, + minHeight, + onChange, +}) { + const onClick = (e) => Utils.clickEffect(e); + if (type === "component") { + return ; + } + if (type === "select") { + return ( + + + ); + } + if (type === "radio") { + return options.map((option) => ( +
+ +
+ )); + } + if (type === "text" || type === "color") { + return ( + +