Skip to content

Commit bf9b0fd

Browse files
Merge pull request #4 from PascalLuginbuehl/icons
select and display @mdi icons
2 parents 1461861 + ea1d020 commit bf9b0fd

File tree

9 files changed

+118
-14
lines changed

9 files changed

+118
-14
lines changed

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@
5050
"@electron-forge/publisher-github": "^6.2.1",
5151
"@emotion/react": "^11.11.1",
5252
"@emotion/styled": "^11.11.0",
53-
"@fluentui/font-icons-mdl2": "^8.5.22",
54-
"@fluentui/react-icons-mdl2": "^1.3.46",
5553
"@fontsource/roboto": "^5.0.3",
5654
"@hookform/resolvers": "^3.1.1",
55+
"@mdi/js": "^7.2.96",
56+
"@mdi/react": "^1.6.1",
57+
"@mdi/svg": "^7.2.96",
5758
"@mui/icons-material": "^5.11.16",
5859
"@mui/material": "^5.13.6",
5960
"@mui/x-date-pickers": "^6.9.0",

src/settings/manage-switches/draggable-list-item.tsx

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import {
2-
Box, IconButton, ListItem, ListItemText,
2+
Box, FilterOptionsState, IconButton, ListItem, ListItemIcon, ListItemText, createFilterOptions,
33
} from '@mui/material';
44
import React, { useState } from 'react';
55
import { Draggable } from 'react-beautiful-dnd';
66
import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutline';
77
import {
8+
AutocompleteElement,
89
TextFieldElement, useWatch,
910
} from 'react-hook-form-mui';
1011
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep';
1112
import DoneOutlineIcon from '@mui/icons-material/DoneOutline';
13+
import Icon from '@mdi/react';
1214
import { IEntityConfig } from '../../store';
1315
import IState from '../../types/state';
1416
import type { TFormValues } from '../entities-form';
1517
import EntityUtils from '../../utils/entity-utils';
18+
import icons, { getIconsPath } from './icons';
1619

1720
interface DraggableListItemProps {
1821
entity: IEntityConfig,
@@ -21,6 +24,11 @@ interface DraggableListItemProps {
2124
onRemove: () => void,
2225
}
2326

27+
const OPTIONS_LIMIT = 100;
28+
const defaultFilterOptions = createFilterOptions();
29+
30+
const filterOptions = (options: unknown[], state: FilterOptionsState<unknown>) => defaultFilterOptions(options, state).slice(0, OPTIONS_LIMIT);
31+
2432
export default function DraggableListItem(props: DraggableListItemProps) {
2533
const {
2634
index, state, onRemove,
@@ -29,6 +37,9 @@ export default function DraggableListItem(props: DraggableListItemProps) {
2937
const [editing, setEditing] = useState<boolean>(false);
3038

3139
const entity = useWatch<TFormValues, `entities.${number}`>({ name: `entities.${index}` });
40+
const autocompleteOptions = icons.map((option) => ({
41+
label: option.name, id: option.name, path: option.path, aliases: option.aliases,
42+
}));
3243

3344
return (
3445
<Draggable
@@ -68,11 +79,47 @@ export default function DraggableListItem(props: DraggableListItemProps) {
6879
>
6980
{
7081
!editing ? (
71-
<ListItemText
72-
primary={EntityUtils.getEntityName(entity, state)}
73-
/>
82+
<>
83+
<ListItemIcon sx={{ minWidth: 36 }}>
84+
{entity.icon && <Icon path={getIconsPath(entity.icon)} size={1} />}
85+
</ListItemIcon>
86+
<ListItemText
87+
primary={EntityUtils.getEntityName(entity, state)}
88+
/>
89+
</>
7490
) : (
75-
<TextFieldElement<TFormValues> name={`entities.${index}.label`} label={state?.attributes.friendly_name ?? entity.entity_id} />
91+
<>
92+
<AutocompleteElement<TFormValues>
93+
name={`entities.${index}.icon`}
94+
options={autocompleteOptions}
95+
textFieldProps={{ fullWidth: true }}
96+
label="Icon"
97+
matchId
98+
autocompleteProps={{
99+
fullWidth: true,
100+
filterOptions,
101+
102+
renderOption: (optionProps, option, { selected }) => (
103+
// eslint-disable-next-line react/jsx-props-no-spreading
104+
<ListItem {...optionProps} key={option.id}>
105+
<ListItemIcon>
106+
<Icon path={option.path} size={1} />
107+
</ListItemIcon>
108+
<ListItemText
109+
primaryTypographyProps={selected ? { fontWeight: 'bold' } : undefined}
110+
primary={option.label}
111+
secondary={option.aliases.join(', ')}
112+
/>
113+
</ListItem>
114+
),
115+
}}
116+
117+
/>
118+
<TextFieldElement<TFormValues>
119+
name={`entities.${index}.label`}
120+
label={state?.attributes.friendly_name ?? entity.entity_id}
121+
/>
122+
</>
76123
)
77124
}
78125
</ListItem>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as icons from '@mdi/js';
2+
import meta from '@mdi/svg/meta.json';
3+
4+
const camelize = (s: string) => s.replace(/-./g, (x) => x[1].toUpperCase());
5+
6+
const metaValues = [...Object.values(meta)];
7+
8+
export const getIconsPath = (iconName: string) => {
9+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
10+
// @ts-ignore
11+
const icon = icons[camelize(`mdi-${iconName}`)];
12+
13+
if (!icon) {
14+
return null;
15+
}
16+
17+
return icon;
18+
};
19+
20+
// Icons can be found here: https://pictogrammers.com/library/mdi/
21+
export default metaValues.map((value) => ({ ...value, path: getIconsPath(value.name) }));

src/settings/manage-switches/manage-switches.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export default function ManageSwitches(props: ManageSwitchesProps) {
8282
return;
8383
}
8484

85-
append({ entity_id: value as string, label: null });
85+
append({ entity_id: value as string, label: null, icon: null });
8686

8787
setSelectFieldValue(null);
8888
}}

src/store.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface IEntityConfig {
66
entity_id: string,
77
// domain: 'switch',
88
// service: 'toggle',
9+
icon: string | null,
910
label: string | null,
1011
}
1112
export interface ISettings {
@@ -50,6 +51,11 @@ const schema: JSONSchemaType<SchemaType> = {
5051
type: ['string', 'null'] as unknown as 'string',
5152
default: null,
5253
},
54+
icon: {
55+
// https://github.com/ajv-validator/ajv/issues/2163#issuecomment-1440299363
56+
type: ['string', 'null'] as unknown as 'string',
57+
default: null,
58+
},
5359
},
5460
required: ['entity_id', 'label'],
5561
},

src/tray/configuration.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
22
import { useQuery } from '@tanstack/react-query';
3-
import { LampIcon } from '@fluentui/react-icons-mdl2';
43
import clsx from 'clsx';
4+
import Icon from '@mdi/react';
5+
import { getIconsPath } from '../settings/manage-switches/icons';
56
import EntityUtils from '../utils/entity-utils';
67

78
export default function Configuration() {
@@ -47,8 +48,12 @@ export default function Configuration() {
4748
await refetch();
4849
}}
4950
>
50-
<LampIcon />
51-
{EntityUtils.getEntityName(entity, state)}
51+
<div className="spacing">
52+
{entity.icon && <Icon path={getIconsPath(entity.icon)} size={1} />}
53+
</div>
54+
<span>
55+
{EntityUtils.getEntityName(entity, state)}
56+
</span>
5257
</button>
5358
);
5459
})

src/tray/css/panel.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,12 @@ body[data-is-win11="true"] {
306306
text-align: left;
307307
background-color: transparent;
308308
color: var(--tray-text);
309+
display: flex;
310+
align-items: center;
311+
& .spacing {
312+
width: 36px;
313+
height: 24px;
314+
}
309315
&.selected {
310316
background-color: #333;
311317
}

src/tray/renderer.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import React from 'react';
22
import { createRoot } from 'react-dom/client';
33
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
4-
import { initializeIcons } from '@fluentui/font-icons-mdl2';
54
import App from './app';
65
import '../i18next';
76

87
import './css/vars.scss';
98
import './css/panel.scss';
109

11-
initializeIcons();
12-
1310
// this element does 100% exist
1411
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1512
const container = window.document.getElementById('app')!;

0 commit comments

Comments
 (0)