diff options
Diffstat (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx')
| -rwxr-xr-x | users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx new file mode 100755 index 0000000..a026047 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/settings/user-widgets-creator.jsx | |||
| @@ -0,0 +1,297 @@ | |||
| 1 | import * as Uebersicht from "uebersicht"; | ||
| 2 | import * as Icons from "../icons/icons.jsx"; | ||
| 3 | import * as Settings from "../../settings"; | ||
| 4 | import * as Utils from "../../utils"; | ||
| 5 | import ColorPicker from "./color-picker.jsx"; | ||
| 6 | import IconPicker from "./icon-picker.jsx"; | ||
| 7 | |||
| 8 | const { React } = Uebersicht; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * UserWidgetsCreator component allows users to create and manage custom widgets. | ||
| 12 | * @param {Object} props - The component props. | ||
| 13 | * @param {Object} props.defaultValue - The default value for the widgets. | ||
| 14 | * @param {Function} props.onChange - The function to call when the widgets change. | ||
| 15 | * @returns {JSX.Element} The UserWidgetsCreator component. | ||
| 16 | */ | ||
| 17 | export default function UserWidgetsCreator({ defaultValue, onChange }) { | ||
| 18 | const [widgets, setWidgets] = React.useState(defaultValue || {}); | ||
| 19 | const keys = Object.keys(widgets); | ||
| 20 | |||
| 21 | // Determine the highest widget ID and calculate the new ID for a new widget | ||
| 22 | const highestId = keys.reduce((acc, key) => { | ||
| 23 | const keyAsNumber = parseInt(key, 10); | ||
| 24 | return keyAsNumber > acc ? keyAsNumber : acc; | ||
| 25 | }, 1); | ||
| 26 | const newId = highestId + 1; | ||
| 27 | |||
| 28 | // Function to add a new widget | ||
| 29 | const onClick = () => | ||
| 30 | setWidgets((widgets) => ({ | ||
| 31 | ...widgets, | ||
| 32 | [newId]: { ...Settings.userWidgetDefault }, | ||
| 33 | })); | ||
| 34 | |||
| 35 | // Function to handle changes to a widget | ||
| 36 | const onWidgetChange = (index, field, value) => { | ||
| 37 | const newWidgets = { ...widgets }; | ||
| 38 | const newKeys = Object.keys(newWidgets); | ||
| 39 | const updatedWidgets = newKeys.reduce((acc, key, i) => { | ||
| 40 | const widget = newWidgets[key]; | ||
| 41 | return { | ||
| 42 | ...acc, | ||
| 43 | [i + 1]: key === index ? { ...widget, [field]: value } : widget, | ||
| 44 | }; | ||
| 45 | }, {}); | ||
| 46 | setWidgets(updatedWidgets); | ||
| 47 | }; | ||
| 48 | |||
| 49 | // Effect to detect changes in widgets and call onChange prop | ||
| 50 | React.useEffect(() => { | ||
| 51 | const diffs = Utils.compareObjects(defaultValue, widgets); | ||
| 52 | const hasDiffs = Object.keys(diffs).length > 0; | ||
| 53 | if (hasDiffs) onChange({ target: { value: widgets } }); | ||
| 54 | }, [defaultValue, onChange, widgets]); | ||
| 55 | |||
| 56 | return ( | ||
| 57 | <div className="user-widgets-creator"> | ||
| 58 | {keys.map((key, i) => ( | ||
| 59 | <UserWidgetCreator | ||
| 60 | key={`${key}-${widgets[key].backgroundColor}`} | ||
| 61 | index={key} | ||
| 62 | onWidgetChange={onWidgetChange} | ||
| 63 | setWidgets={setWidgets} | ||
| 64 | widget={widgets[key]} | ||
| 65 | isFirst={i === 0} | ||
| 66 | isLast={i === keys.length - 1} | ||
| 67 | /> | ||
| 68 | ))} | ||
| 69 | <button className="user-widgets-creator__add" onClick={onClick}> | ||
| 70 | <Icons.Add /> | ||
| 71 | Add a custom widget | ||
| 72 | </button> | ||
| 73 | </div> | ||
| 74 | ); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * UserWidgetCreator component allows users to configure individual widgets. | ||
| 79 | * @param {Object} props - The component props. | ||
| 80 | * @param {string} props.index - The index of the widget. | ||
| 81 | * @param {boolean} props.isFirst - Whether the widget is the first in the list. | ||
| 82 | * @param {boolean} props.isLast - Whether the widget is the last in the list. | ||
| 83 | * @param {Function} props.onWidgetChange - The function to call when the widget changes. | ||
| 84 | * @param {Function} props.setWidgets - The function to set the widgets state. | ||
| 85 | * @param {Object} props.widget - The widget data. | ||
| 86 | * @returns {JSX.Element} The UserWidgetCreator component. | ||
| 87 | */ | ||
| 88 | function UserWidgetCreator({ | ||
| 89 | index, | ||
| 90 | isFirst, | ||
| 91 | isLast, | ||
| 92 | onWidgetChange, | ||
| 93 | setWidgets, | ||
| 94 | widget, | ||
| 95 | }) { | ||
| 96 | const { | ||
| 97 | title, | ||
| 98 | icon, | ||
| 99 | backgroundColor, | ||
| 100 | output, | ||
| 101 | onClickAction, | ||
| 102 | onRightClickAction, | ||
| 103 | onMiddleClickAction, | ||
| 104 | refreshFrequency, | ||
| 105 | active = true, | ||
| 106 | noIcon = false, | ||
| 107 | hideWhenNoOutput = true, | ||
| 108 | showOnDisplay = "", | ||
| 109 | } = widget; | ||
| 110 | |||
| 111 | const indexAsNumber = parseInt(index, 10); | ||
| 112 | |||
| 113 | // Function to remove a widget | ||
| 114 | const onRemoveClick = () => { | ||
| 115 | setWidgets((widgets) => { | ||
| 116 | const keys = Object.keys(widgets); | ||
| 117 | return keys.reduce( | ||
| 118 | (acc, key) => (key === index ? acc : { ...acc, [key]: widgets[key] }), | ||
| 119 | {}, | ||
| 120 | ); | ||
| 121 | }); | ||
| 122 | }; | ||
| 123 | |||
| 124 | // Function to handle changes to widget fields | ||
| 125 | const onChange = (field, chexbox = false) => { | ||
| 126 | return (e) => { | ||
| 127 | const value = (chexbox ? e?.target?.checked : e?.target?.value) ?? ""; | ||
| 128 | onWidgetChange(index, field, value); | ||
| 129 | }; | ||
| 130 | }; | ||
| 131 | |||
| 132 | // Function to move the widget up in the list | ||
| 133 | const onBeforeClick = () => { | ||
| 134 | setWidgets((widgets) => { | ||
| 135 | const swapedWidget = widgets[indexAsNumber - 1]; | ||
| 136 | return { | ||
| 137 | ...widgets, | ||
| 138 | [indexAsNumber - 1]: widget, | ||
| 139 | [indexAsNumber]: swapedWidget, | ||
| 140 | }; | ||
| 141 | }); | ||
| 142 | }; | ||
| 143 | |||
| 144 | // Function to move the widget down in the list | ||
| 145 | const onAfterClick = () => { | ||
| 146 | setWidgets((widgets) => { | ||
| 147 | const swapedWidget = widgets[indexAsNumber + 1]; | ||
| 148 | return { | ||
| 149 | ...widgets, | ||
| 150 | [indexAsNumber + 1]: widget, | ||
| 151 | [indexAsNumber]: swapedWidget, | ||
| 152 | }; | ||
| 153 | }); | ||
| 154 | }; | ||
| 155 | |||
| 156 | return ( | ||
| 157 | <div key={indexAsNumber} className="user-widget-creator"> | ||
| 158 | <div className="user-widget-creator__sort-buttons"> | ||
| 159 | {!isFirst && ( | ||
| 160 | <button | ||
| 161 | className="user-widget-creator__sort-button user-widget-creator__sort-button--before" | ||
| 162 | onClick={onBeforeClick} | ||
| 163 | > | ||
| 164 | <Icons.ChevronTop /> | ||
| 165 | </button> | ||
| 166 | )} | ||
| 167 | {!isLast && ( | ||
| 168 | <button | ||
| 169 | className="user-widget-creator__sort-button user-widget-creator__sort-button--after" | ||
| 170 | onClick={onAfterClick} | ||
| 171 | > | ||
| 172 | <Icons.ChevronBottom /> | ||
| 173 | </button> | ||
| 174 | )} | ||
| 175 | </div> | ||
| 176 | <button className="user-widget-creator__remove" onClick={onRemoveClick}> | ||
| 177 | <Icons.Remove /> | ||
| 178 | </button> | ||
| 179 | <div className="user-widget-creator__index"> | ||
| 180 | n°<b>{index}</b> | ||
| 181 | </div> | ||
| 182 | <IconPicker callback={onWidgetChange} index={index} selectedIcon={icon} /> | ||
| 183 | <div className="user-widget-creator__right"> | ||
| 184 | <div className="user-widget-creator__right-top"> | ||
| 185 | <ColorPicker | ||
| 186 | callback={onWidgetChange} | ||
| 187 | index={index} | ||
| 188 | selectedColor={backgroundColor} | ||
| 189 | /> | ||
| 190 | <input | ||
| 191 | className="user-widget-creator__title" | ||
| 192 | onChange={onChange("title")} | ||
| 193 | type="text" | ||
| 194 | defaultValue={title} | ||
| 195 | /> | ||
| 196 | <label htmlFor={`refresh-frequency-${index}`}> | ||
| 197 | Refresh frequency (ms): | ||
| 198 | </label> | ||
| 199 | <input | ||
| 200 | className="user-widget-creator__refresh-frequency" | ||
| 201 | onChange={onChange("refreshFrequency")} | ||
| 202 | id={`refresh-frequency-${index}`} | ||
| 203 | type="text" | ||
| 204 | defaultValue={refreshFrequency} | ||
| 205 | /> | ||
| 206 | </div> | ||
| 207 | <div className="user-widget-creator__input-group"> | ||
| 208 | <label htmlFor={`show-on-display-${index}`}>Show on display: </label> | ||
| 209 | <input | ||
| 210 | className="user-widget-creator__show-on-display" | ||
| 211 | onChange={onChange("showOnDisplay")} | ||
| 212 | id={`show-on-display-${index}`} | ||
| 213 | type="text" | ||
| 214 | defaultValue={showOnDisplay} | ||
| 215 | placeholder="example: 1,2 (leave blank to show on all displays)" | ||
| 216 | spellCheck={false} | ||
| 217 | /> | ||
| 218 | </div> | ||
| 219 | <div className="user-widget-creator__input-group"> | ||
| 220 | <label htmlFor={`output-${index}`}>Command/script path: </label> | ||
| 221 | <input | ||
| 222 | className="user-widget-creator__output" | ||
| 223 | onChange={onChange("output")} | ||
| 224 | id={`output-${index}`} | ||
| 225 | type="text" | ||
| 226 | defaultValue={output} | ||
| 227 | spellCheck={false} | ||
| 228 | /> | ||
| 229 | </div> | ||
| 230 | <div className="user-widget-creator__input-group"> | ||
| 231 | <label htmlFor={`on-click-action-${index}`}> | ||
| 232 | On click command/script path:{" "} | ||
| 233 | </label> | ||
| 234 | <input | ||
| 235 | className="user-widget-creator__on-click-action" | ||
| 236 | onChange={onChange("onClickAction")} | ||
| 237 | id={`on-click-action-${index}`} | ||
| 238 | type="text" | ||
| 239 | defaultValue={onClickAction} | ||
| 240 | spellCheck={false} | ||
| 241 | /> | ||
| 242 | </div> | ||
| 243 | <div className="user-widget-creator__input-group"> | ||
| 244 | <label htmlFor={`on-right-click-action-${index}`}> | ||
| 245 | On right click command/script path:{" "} | ||
| 246 | </label> | ||
| 247 | <input | ||
| 248 | className="user-widget-creator__on-right-click-action" | ||
| 249 | onChange={onChange("onRightClickAction")} | ||
| 250 | id={`on-right-click-action-${index}`} | ||
| 251 | type="text" | ||
| 252 | defaultValue={onRightClickAction} | ||
| 253 | spellCheck={false} | ||
| 254 | /> | ||
| 255 | </div> | ||
| 256 | <div className="user-widget-creator__input-group"> | ||
| 257 | <label htmlFor={`on-middle-click-action-${index}`}> | ||
| 258 | On middle click command/script path:{" "} | ||
| 259 | </label> | ||
| 260 | <input | ||
| 261 | className="user-widget-creator__on-middle-click-action" | ||
| 262 | onChange={onChange("onMiddleClickAction")} | ||
| 263 | id={`on-middle-click-action-${index}`} | ||
| 264 | type="text" | ||
| 265 | defaultValue={onMiddleClickAction} | ||
| 266 | spellCheck={false} | ||
| 267 | /> | ||
| 268 | </div> | ||
| 269 | <div className="user-widget-creator__input-group"> | ||
| 270 | <input | ||
| 271 | id={`active-${index}`} | ||
| 272 | type="checkbox" | ||
| 273 | defaultChecked={active} | ||
| 274 | onChange={onChange("active", true)} | ||
| 275 | /> | ||
| 276 | <label htmlFor={`active-${index}`}>Active</label> | ||
| 277 | <input | ||
| 278 | id={`no-icon-${index}`} | ||
| 279 | type="checkbox" | ||
| 280 | defaultChecked={noIcon} | ||
| 281 | onChange={onChange("noIcon", true)} | ||
| 282 | /> | ||
| 283 | <label htmlFor={`no-icon-${index}`}>No icon</label> | ||
| 284 | <input | ||
| 285 | id={`hide-when-no-output-${index}`} | ||
| 286 | type="checkbox" | ||
| 287 | defaultChecked={hideWhenNoOutput} | ||
| 288 | onChange={onChange("hideWhenNoOutput", true)} | ||
| 289 | /> | ||
| 290 | <label htmlFor={`hide-when-no-output-${index}`}> | ||
| 291 | Hide when no script output | ||
| 292 | </label> | ||
| 293 | </div> | ||
| 294 | </div> | ||
| 295 | </div> | ||
| 296 | ); | ||
| 297 | } | ||
