Тематика пользовательского интерфейса React Fluent
В настоящее время я разрабатываю приложение для MS Teams. После привыкания к библиотеке MS UI (теперь она называется fluent-ui) все начинает собираться воедино. Поскольку я хочу предоставить своим пользователям "встроенный" опыт, я хочу использовать собственные темы и стили, которые также использует Microsoft.
В качестве основы я использую @fluentui/react-northstar, который официально поддерживается командами, проверьте здесь.И, что самое приятное, в нем есть встроенные темы для команд. Потрясающие.
Все идет нормально. Я также хочу показать несколько списков, в которых отображаются данные текущего вошедшего в систему пользователя. Для этого я хотел использовать Shimmer-DetailsList. Проблема здесь в том, что я не могу применять темы из @fluentui/react-northstar, что сводит меня с ума. Кто-нибудь знает, что я могу здесь сделать? Это сводит меня с ума от того, что предоставленные пользовательские интерфейсы несовместимы друг с другом или, может быть, я использую неправильный подход.
Я также могу показать код, если захочу, но на этом этапе я просто пытаюсь понять.
Большое спасибо за вашу помощь, ребята!
С уважением, Том
Обновление: хорошо, может быть немного сложно, но я изо всех сил стараюсь показать свой код. Обрежу ненужные части. Код написан для React.
Index.js Здесь я определяю, какая тема активна у пользователя, что можно сделать с помощьюwindow.location.search
. Затем тема также передается в App.js в качестве опоры.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider, themes } from '@fluentui/react-northstar'
...
const teamsTheme = updateTheme(getQueryVariable('theme'));
//const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const rootElement = document.getElementById('root');
console.log("theme in index: " + teamsTheme);
ReactDOM.render(
<Provider theme={teamsTheme}>
<App theme={teamsTheme}/>
</Provider>,
rootElement
);
App.js В App.js я определяю некоторые общие вещи. Я проверяю, могут ли команды аутентифицировать пользователя, а также принял ли пользователь TOU при первом использовании (запрос к моему API). Если все в порядке, я визуализирую настраиваемый компонент под названием content, где также передаю тему как опору. Я оставлю общие объявления функций, потому что они в настоящее время не вызывают проблемы.
import React, { Component } from 'react';
import TouService from "./services/touService";
import { Stack } from 'office-ui-fabric-react'
import { Loader } from '@fluentui/react-northstar'
import "./css/settings.css"
import Content from "./components/Content"
import AcceptTou from "./components/AcceptTou"
const microsoftTeams = require('@microsoft/teams-js');
export default class App extends Component {
static displayName = App.name;
constructor(props) {
super(props)
this.touService = new TouService();
this.state = {
userIsAuthenticated: false,
authenticationLoading: true,
authMessage: "Unauthorized!!!",
currentUser: "",
tokenString: "",
touAccepted: false,
touLoading: true
};
**some functions here**
componentDidMount() {
this.initializeTeams();
}
render() {
const loading = (this.state.authenticationLoading || this.state.touLoading || (this.state.touAccepted === false)) ?
<div>
<br />
<Loader label='Loading...' />
<br />
</div> :
<Content theme={this.props.theme}/>;
const tou = (this.state.userIsAuthenticated && !this.state.authenticationLoading && !this.state.touLoading && !this.state.touAccepted) &&
<AcceptTou currentUser={this.state.currentUser} touAccepted={this.state.touAccepted} theme={this.props.theme} onTouChange={this.touAcceptedChanged}/>;
//console.logconsole.log(this.state);
return (
<Stack
horizontalAlign="center"
verticalFill
styles={{
root: {
width: this.width,
height: this.height,
margin: '0 auto',
textAlign: 'center',
color: '#605e5c'
}
}}>
{loading}
{tou}
</Stack>
);
}
}
Content.js В контенте я определяю общий контент для макета страницы.
import React, { Component } from 'react';
import AssetMenu from "../UI/AssetMenu"
import Layout from "../UI/Layout"
import ToolbarMenu from '../UI/ToolbarMenu';
import ShimmerApplicationExample from "./List"
export default class Content extends React.Component {
constructor(props) {
super(props)
console.log("theme in content.js: " + this.props.theme);
}
render() {
const content = "my Content";
return (
<Layout header={<AssetMenu />} toolbar={<ToolbarMenu />} content={<ShimmerApplicationExample theme={this.props.theme}/>} />
);
}
}
Layout.js обрабатывает общий макет страницы. что в настоящее время не идеально, потому что я все еще учусь, но это можно сделать позже.
import React from 'react';
import { Flex, Segment, Divider } from '@fluentui/react-northstar'
export default class Layout extends React.Component {
//<Grid columns="repeat(3, 1fr)" rows="50px 150px 50px">
render() {
return (
<Flex column fill>
{this.props.header}
{this.props.toolbar}
<Segment content={this.props.content} />
</Flex>
);
}
}
ToolbarMenu.js предоставляет панель инструментов для взаимодействия пользователя или создания некоторого контента. Но функционала пока нет.
import React from 'react';
import { Menu, menuAsToolbarBehavior } from '@fluentui/react-northstar';
const items = [
{
key: 'add',
icon: {
name: 'add',
outline: true,
},
content: "New",
'aria-label': 'add',
},
{
key: 'edit',
icon: {
name: 'edit',
outline: true,
},
content: "Edit",
'aria-label': 'Edit Tool',
},
{
key: 'delete',
icon: {
name: 'trash-can',
outline: true,
},
content: "Delete",
'aria-label': 'Delete Tool',
},
];
export default class ToolbarMenu extends React.Component {
render() {
return (
<Menu
defaultActiveIndex={0}
items={items}
primary
iconOnly
accessibility={menuAsToolbarBehavior}
aria-label="Compose Editor"
/>
)
}
}
List.tsx должен предоставлять список деталей для пользовательского контента. Вот где сейчас проблема. Код в основном пример от MS здесь:. За исключением того, что я пытался использовать свою тему из Index.js здесь, в списке, но это не работает.
import * as React from 'react';
import { Async } from 'office-ui-fabric-react/lib/Utilities';
import { createListItems, IExampleItem } from '@uifabric/example-data';
import { IColumn, buildColumns, SelectionMode, Toggle } from 'office-ui-fabric-react/lib/index';
import { ShimmeredDetailsList } from 'office-ui-fabric-react/lib/ShimmeredDetailsList';
const fileIcons: { name: string }[] = [
{ name: 'accdb' },
{ name: 'csv' },
{ name: 'docx' },
{ name: 'dotx' },
{ name: 'mpt' },
{ name: 'odt' },
{ name: 'one' },
{ name: 'onepkg' },
{ name: 'onetoc' },
{ name: 'pptx' },
{ name: 'pub' },
{ name: 'vsdx' },
{ name: 'xls' },
{ name: 'xlsx' },
{ name: 'xsn' }
];
const ITEMS_COUNT = 200;
const INTERVAL_DELAY = 2500;
let _items: IExampleItem[];
export interface IShimmerApplicationExampleState {
items: IExampleItem[]; // DetailsList `items` prop is required so it expects at least an empty array.
columns?: IColumn[];
isDataLoaded?: boolean;
}
interface IShimmerListProps {
theme?: any;
}
export default class ShimmerApplicationExample extends React.Component<IShimmerListProps, IShimmerApplicationExampleState> {
private _lastIntervalId: number = 0;
private _lastIndexWithData: number = 0;
private _async: Async;
constructor(props: {}) {
super(props);
this.state = {
items: [],
columns: _buildColumns(),
isDataLoaded: false
};
this._async = new Async(this);
console.log("theme in shimmer list consturctor: " + this.props.theme);
}
public componentWillUnmount(): void {
this._async.dispose();
}
public render(): JSX.Element {
const { items, columns, isDataLoaded } = this.state;
const theme = this.props.theme;
return (
<div>
<Toggle
theme = {theme}
label="Toggle to load content"
style={{ display: 'block', marginBottom: '20px' }}
checked={isDataLoaded}
onText="Content"
offText="Shimmer"
/>
<div>
<ShimmeredDetailsList
theme = {theme}
setKey="items"
items={items}
columns={columns}
selectionMode={SelectionMode.none}
enableShimmer={!isDataLoaded}
ariaLabelForShimmer="Content is being fetched"
ariaLabelForGrid="Item details"
listProps={{ renderedWindowsAhead: 0, renderedWindowsBehind: 0 }}
/>
</div>
</div>
);
}
private _loadData = (): void => {
this._lastIntervalId = this._async.setInterval(() => {
const randomQuantity: number = Math.floor(Math.random() * 10) + 1;
const itemsCopy = this.state.items!.slice(0);
itemsCopy.splice(
this._lastIndexWithData,
randomQuantity,
..._items.slice(this._lastIndexWithData, this._lastIndexWithData + randomQuantity)
);
this._lastIndexWithData += randomQuantity;
this.setState({
items: itemsCopy
});
}, INTERVAL_DELAY);
};
private _onLoadData = (ev: React.MouseEvent<HTMLElement>, checked: boolean): void => {
if (!_items) {
_items = createListItems(ITEMS_COUNT);
_items.map((item: IExampleItem) => {
const randomFileType = this._randomFileIcon();
item.thumbnail = randomFileType.url;
});
}
let items: IExampleItem[];
const randomQuantity: number = Math.floor(Math.random() * 10) + 1;
if (checked) {
items = _items.slice(0, randomQuantity).concat(new Array(ITEMS_COUNT - randomQuantity));
this._lastIndexWithData = randomQuantity;
this._loadData();
} else {
items = [];
this._async.clearInterval(this._lastIntervalId);
}
this.setState({
isDataLoaded: checked,
items: items
});
};
private _onRenderItemColumn = (item: IExampleItem, index: number, column: IColumn): JSX.Element | string | number => {
if (column.key === 'thumbnail') {
return <img src={item.thumbnail} />;
}
return item[column.key as keyof IExampleItem];
};
private _randomFileIcon(): { docType: string; url: string } {
const docType: string = fileIcons[Math.floor(Math.random() * fileIcons.length) + 0].name;
return {
docType,
url: `https://static2.sharepointonline.com/files/fabric/assets/brand-icons/document/svg/${docType}_16x1.svg`
};
}
}
function _buildColumns(): IColumn[] {
const _item = createListItems(1);
const columns: IColumn[] = buildColumns(_item);
for (const column of columns) {
if (column.key === 'thumbnail') {
column.name = 'FileType';
column.minWidth = 16;
column.maxWidth = 16;
column.isIconOnly = true;
column.iconName = 'Page';
break;
}
}
return columns;
}
Для лучшего понимания: Microsoft говорит, что этот пакет (fluentui/react-northstar) лучше всего подходит для поддержки Teams, потому что в нем есть все, а также есть интегрированные темы для команд, но он не предоставляет DetailsList как office-ui-response (который должен быть в на самом деле библиотеки почти одинаковые?) и они тоже между собой не совместимы?
Я понятия не имею, что я сделал не так, и, если возможно, я бы предпочел не определять свои собственные темы, чем использовать уже существующие.