mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-12 02:09:24 +08:00
187 lines
6.6 KiB
Markdown
187 lines
6.6 KiB
Markdown
|
# Web project for the Settings UI
|
||
|
|
||
|
## Introduction
|
||
|
|
||
|
This project generates the web UI shown in the [PowerToys Settings](/src/editor).
|
||
|
It's a `ReactJS` project created using [UI Fabric](https://developer.microsoft.com/en-us/fabric#/).
|
||
|
|
||
|
## Build Commands
|
||
|
|
||
|
Here are the commands to build and test this project:
|
||
|
|
||
|
### To start the development server
|
||
|
|
||
|
```
|
||
|
npm install
|
||
|
npm run start
|
||
|
```
|
||
|
|
||
|
### Building and integrating into PowerToys settings project
|
||
|
|
||
|
```
|
||
|
npm run build
|
||
|
```
|
||
|
|
||
|
## Updating the icons
|
||
|
|
||
|
Icons inside [`src/icons/`](./src/icons/) were generated from the [Office UI Fabric Icons subset generation tool.](https://uifabricicons.azurewebsites.net/)
|
||
|
|
||
|
In case the subset needs to be changed, additional steps are needed to include the icon font in the built `dist/bundle.js`:
|
||
|
- Copy the inline font data taken from [`src/icons/css/fabric-icons-inline.css`](src/icons/css/fabric-icons-inline.css) and place it in the `fontFace` `src` value in [`src/icons/src/fabric-icons.ts`](src/icons/src/fabric-icons.ts).
|
||
|
|
||
|
A list of the current icons in the subset can be seen in the `icons` object in [`src/icons/src/fabric-icons.ts`](src/icons/src/fabric-icons.ts).
|
||
|
|
||
|
SVG icons, including the icons for each PowerToy listed in the Settings, are contained in [`src/svg/`](src/svg/). To add additional SVG icons add them to [`src/svg/`](src/svg/) and register them in [`src/setup_icons.tsx`](src/setup_icons.tsx).
|
||
|
|
||
|
## Code Structure
|
||
|
|
||
|
The project structure is based on the [`UI Fabric` scaffold](https://developer.microsoft.com/en-us/fabric#/get-started/web#option-1-quick-start) obtained by initializing it with `npm init uifabric`.
|
||
|
|
||
|
#### [index.html](./index.html)
|
||
|
The HTML entry-point of the project.
|
||
|
Loads the `ReactJS` distribution script.
|
||
|
Defines JavaScript functions to receive and send messages to the [PowerToys Settings](/src/editor) window.
|
||
|
|
||
|
#### [src/index.tsx](./src/index.tsx)
|
||
|
Main `ReactJS` entrypoint, initializing the `ReactDOM`.
|
||
|
|
||
|
#### [src/setup_icons.tsx](./src/setup_icons.tsx)
|
||
|
Defines the `setup_powertoys_icons` function that registers the icons to be used in the components.
|
||
|
|
||
|
#### [src/components/](./src/components/)
|
||
|
Contains the `ReactJS` components, including the Settings controls for each type of setting.
|
||
|
|
||
|
#### [src/components/App.tsx](./src/components/App.tsx)
|
||
|
Defines the main App component, containing the UI layout, navigation menu, dialogs and main load/save logic.
|
||
|
|
||
|
#### [src/components/GeneralSettings.tsx](./src/components/GeneralSettings.tsx)
|
||
|
Defines the PowerToys General Settings component, including logic to construct the object sent to PowerToys to change the General settings.
|
||
|
|
||
|
#### [src/components/CustomSettingsScreen.tsx](./src/components/CustomSettingsScreen.tsx)
|
||
|
Defines the component that generates the settings screen for a PowerToy depending on its settings definition.
|
||
|
|
||
|
#### [src/components/BaseSettingsControl.tsx](./src/components/BaseSettingsControl.tsx)
|
||
|
Defines the base class for a Settings control.
|
||
|
|
||
|
#### [src/css/layout.css](./src/css/layout.css)
|
||
|
General layout styles.
|
||
|
|
||
|
#### [src/icons/](./src/icons/)
|
||
|
Icons generated from the [Office UI Fabric Icons subset generation tool.](https://uifabricicons.azurewebsites.net/)
|
||
|
|
||
|
#### [src/svg/](./src/svg/)
|
||
|
SVG icon assets.
|
||
|
|
||
|
## Creating a new settings control
|
||
|
|
||
|
The [`BaseSettingsControl` class](./src/components/BaseSettingsControl.tsx) can be extended to create a new Settings control type.
|
||
|
|
||
|
```tsx
|
||
|
export class BaseSettingsControl extends React.Component <any, any> {
|
||
|
parent_on_change: Function;
|
||
|
constructor(props:any) {
|
||
|
super(props);
|
||
|
this.parent_on_change=props.on_change;
|
||
|
}
|
||
|
public get_value():any {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
A settings control overrides the `get_value` function to return the value to be used for the Setting the control is representing.
|
||
|
It will use the `parent_on_change` property to signal that the user made some changes to the settings.
|
||
|
|
||
|
Here's the [`StringTextSettingsControl`](./src/components/StringTextSettingsControl.tsx) component to serve as an example:
|
||
|
|
||
|
```tsx
|
||
|
export class StringTextSettingsControl extends BaseSettingsControl {
|
||
|
textref:any = null; // Keeps a reference to the corresponding TextField in the DOM.
|
||
|
|
||
|
constructor(props:any) {
|
||
|
super(props);
|
||
|
this.textref = null;
|
||
|
this.state={
|
||
|
property_values: props.setting
|
||
|
}
|
||
|
}
|
||
|
|
||
|
componentWillReceiveProps(props: any) {
|
||
|
// Fully controlled component.
|
||
|
// Reacting to a property change so that the control is redrawn properly.
|
||
|
this.setState({ property_values: props.setting })
|
||
|
}
|
||
|
|
||
|
public get_value() : any {
|
||
|
// Returns the TextField value.
|
||
|
return {value: this.textref.value};
|
||
|
}
|
||
|
|
||
|
public render(): JSX.Element {
|
||
|
// Renders a UI Fabric TextField.
|
||
|
return (
|
||
|
<TextField
|
||
|
onChange = {
|
||
|
(_event,_new_value) => {
|
||
|
// Updates the state with the new value introduced in the TextField.
|
||
|
this.setState( (prev_state:any) => ({
|
||
|
property_values: {
|
||
|
...(prev_state.property_values),
|
||
|
value: _new_value
|
||
|
}
|
||
|
})
|
||
|
);
|
||
|
// Signal the parent that the user changed a value.
|
||
|
this.parent_on_change();
|
||
|
}
|
||
|
}
|
||
|
value={this.state.property_values.value}
|
||
|
label={this.state.property_values.display_name}
|
||
|
componentRef= {(input) => {this.textref=input;}}
|
||
|
/>
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Each settings property has a `editor_type` field that's used to differentiate between the Settings control types:
|
||
|
```js
|
||
|
'test string_text': {
|
||
|
display_name: 'This is what a string_text looks like',
|
||
|
editor_type: 'string_text',
|
||
|
value: 'A sample string value'
|
||
|
}
|
||
|
```
|
||
|
|
||
|
A new Settings control component can be added to [`src/components/`](./src/components/).
|
||
|
To render the new Settings control, its `editor_type` and component instance need to be added to the [`CustomSettingsScreen` component render()](./src/components/CustomSettingsScreen.tsx):
|
||
|
```tsx
|
||
|
import React from 'react';
|
||
|
import {StringTextSettingsControl} from './StringTextSettingsControl';
|
||
|
|
||
|
...
|
||
|
|
||
|
export class CustomSettingsScreen extends React.Component <any, any> {
|
||
|
references: any;
|
||
|
parent_on_change: Function;
|
||
|
...
|
||
|
public render(): JSX.Element {
|
||
|
let power_toys_properties = this.state.powertoy.properties;
|
||
|
return (
|
||
|
<Stack tokens={{childrenGap:20}}>
|
||
|
...
|
||
|
{
|
||
|
Object.keys(power_toys_properties).
|
||
|
map( (key) => {
|
||
|
switch(power_toys_properties[key].editor_type) {
|
||
|
...
|
||
|
case 'string_text':
|
||
|
return <StringTextSettingsControl
|
||
|
setting = {power_toys_properties[key]}
|
||
|
key={key}
|
||
|
on_change={this.parent_on_change}
|
||
|
ref={(input) => {this.references[key]=input;}}
|
||
|
/>;
|
||
|
...
|
||
|
```
|